<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Peter Nehrer</title>
    <description>The latest articles on DEV Community by Peter Nehrer (@pnehrer).</description>
    <link>https://dev.to/pnehrer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F391168%2F206803fc-14e2-4f2b-bcd2-18e34d12b3d9.png</url>
      <title>DEV Community: Peter Nehrer</title>
      <link>https://dev.to/pnehrer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pnehrer"/>
    <language>en</language>
    <item>
      <title>How to Remove an Unwanted Time Zone from MacOS Calendar</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Tue, 06 Aug 2024 15:09:54 +0000</pubDate>
      <link>https://dev.to/pnehrer/how-to-remove-an-unwanted-time-zone-from-macos-calendar-4hi0</link>
      <guid>https://dev.to/pnehrer/how-to-remove-an-unwanted-time-zone-from-macos-calendar-4hi0</guid>
      <description>&lt;p&gt;In this entirely global world of software development, you likely find yourself working with people across multiple time zones. Keeping available time slots, meeting times, and working hour boundaries straight becomes a challenge when more time zones are added to your daily activities; adding or subtracting three hours between the East Coast and the West Coast becomes a habit, but anything more might give you a headache.&lt;/p&gt;

&lt;p&gt;So you relent and start adding the requisite time zones to your MacOS Calendar, but...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qwha5h4f1ialgtzk3ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qwha5h4f1ialgtzk3ma.png" alt="Select a new time zone"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;*sigh*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You accidentally add the wrong one. Or, one blissfully departs from your daily life, and you certainly don't need the clutter in the Time Zone drop down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22oxwpc6t6qkc1jfpbo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22oxwpc6t6qkc1jfpbo9.png" alt="Time Zones"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No worries, just remove it, but... um, how??&lt;/p&gt;

&lt;p&gt;The Calendar team, with all their legendary UX prowess, seem to have inconveniently omitted that capability!&lt;/p&gt;

&lt;p&gt;After some googling around, all you find is some partially helpful advice on how to reset all Calendar settings by deleting this file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;~/Library/Preferences/com.apple.iCal.plist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Great, but do you have to lose all settings??&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://discussions.apple.com/thread/255397865" rel="noopener noreferrer"&gt;Apple Support article&lt;/a&gt; advises deleting a specific property:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;defaults delete com.apple.iCal 'RecentlyUsedTimeZones'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But maybe you can do one better -- just edit the list in the Xcode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open your Calendar and select a time zone that you want to keep (i.e., not one you will be removing). Quit the Calendar app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your Terminal, type:&lt;br&gt;
&lt;code&gt;open ~/Library/Preferences/com.apple.iCal.plist&lt;/code&gt;&lt;br&gt;
This will open Xcode.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Xcode, expand the property named &lt;code&gt;RecentlyUsedTimeZones&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2ykdmayxxz41xjl1g7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2ykdmayxxz41xjl1g7l.png" alt="Property Editor"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate the time zone you wish to remove and replace it with the last time zone in the list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove the last time zone in the list by clicking the minus sign next to the item key&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tada! When you open your Calendar again, everything should be as you hoped.&lt;/p&gt;

&lt;p&gt;And never think of this minor annoyance again! 😂&lt;/p&gt;

</description>
      <category>howto</category>
      <category>productivity</category>
      <category>calendar</category>
      <category>comedy</category>
    </item>
    <item>
      <title>Create a Custom GitHub Action in Rust</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Mon, 29 Apr 2024 04:29:54 +0000</pubDate>
      <link>https://dev.to/pnehrer/create-a-custom-github-action-in-rust-2al1</link>
      <guid>https://dev.to/pnehrer/create-a-custom-github-action-in-rust-2al1</guid>
      <description>&lt;p&gt;Anyone who gets into serious software development quickly realizes that the path to productivity leads through automation. This applies to both individuals as well as entire teams. When you automate repetitive development tasks, not only do you save time for your future self, but you also reduce human error, foster repeatability, and eliminate the bus factor.&lt;/p&gt;

&lt;p&gt;Of course, there's a fine balance between automating too little and too much -- you probably don't want to spend way more time automating your tasks than it would actually take to perform them manually! This is where your tools and platforms come into play. While some tasks are "standard practices" that apply to every project of a certain type, and may be set up right at the start, high-value projects tend to evolve in unique ways that eventually require a great deal of customization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are GitHub Actions?
&lt;/h2&gt;

&lt;p&gt;If you've used GitHub while working on a software project, chances are you've seen &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; being utilized for automating tasks such as enforcing code formatting, running unit tests, checking code quality, or even packaging and publishing the final software product.&lt;/p&gt;

&lt;p&gt;As an integral part of the GitHub platform, GitHub Actions are tightly integrated into your software development life-cycle, right where your code lives, and where much of your daily product development and collaboration happens. They are quite intuitive and easy to grasp; they use &lt;a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#the-components-of-github-actions" rel="noopener noreferrer"&gt;domain language&lt;/a&gt; that you're likely familiar with, such as workflows, jobs, and steps. Your workflows are described in YAML files that are stored in Git along with the rest of your code. Best of all, they are free to use, and GitHub even provides &lt;a href="https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration" rel="noopener noreferrer"&gt;free compute infrastructure&lt;/a&gt; to run your workflows!&lt;/p&gt;

&lt;p&gt;In addition to a set of commonly required actions provided directly by the platform itself, GitHub supports a rich &lt;a href="https://github.com/marketplace?type=actions" rel="noopener noreferrer"&gt;marketplace&lt;/a&gt; of third-party actions that seem to cover any engineering need under the sun. However, GitHub also allows you to create your own custom actions suitable for your project's specific needs. Of course, if you feel that your custom GitHub Action can be useful to others, you may even publish it to GitHub Marketplace!&lt;/p&gt;

&lt;p&gt;There are a number of reasons you may find yourself in need of a custom GitHub Action. For instance, your software project workflow may require some specific tasks that aren't already available. You may also want to optimize your workflow, reducing the wait time or resources needed to perform the tasks. Or you may need to integrate with external systems that are internal to your team or company. Finally, you may want to develop and offer custom GitHub Actions for profit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom GitHub Actions
&lt;/h2&gt;

&lt;p&gt;While it's possible to build custom GitHub Actions in JavaScript, this approach imposes limits on what they can do. For example, they run directly on the runner and can only use binaries that are part of the runner image. The language itself may be a less-than-ideal choice for non-JavaScript developers.&lt;/p&gt;

&lt;p&gt;You can also create composite actions, which consist of steps that use other actions, including shell scripts. However, if you prefer to write your action in any other language, GitHub allows you to supply the implementation as a &lt;a href="https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action" rel="noopener noreferrer"&gt;Docker container image&lt;/a&gt;; when used in a job step, the runner will execute the action in its own Docker container.&lt;/p&gt;

&lt;p&gt;For best results, you should place your custom action in its own GitHub repository. This will make it easier to share and evolve as its own project.&lt;/p&gt;

&lt;p&gt;What defines a custom GitHub Action is a YAML descriptor file named &lt;a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions" rel="noopener noreferrer"&gt;action.yaml&lt;/a&gt;, which must reside in the root of the action's repository. It contains both required and optional pieces of data, such as the action's name, description, and how it runs -- essentially its type.&lt;/p&gt;

&lt;p&gt;Docker-based actions require a &lt;a href="https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt; to build the action's executable and package it into an image. In your action's descriptor, you can supply the path to your Dockerfile or the full Docker image URI. As you get started with developing your action, it may be convenient to specify the path to the Dockerfile; however, when you actually use your action in production, you'll benefit from building the image ahead of time and publishing it to a Docker registry (e.g., GitHub's own &lt;a href="https://ghcr.io" rel="noopener noreferrer"&gt;Container Registry&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There are several mechanisms that your Docker-based action can use to &lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions" rel="noopener noreferrer"&gt;interact with the platform&lt;/a&gt;. For instance, the platform will set up your container with environment variables that point to shared files for your action's output parameters, job summary, and even environment variables to set up for subsequent steps.&lt;/p&gt;

&lt;p&gt;Custom GitHub Actions may utilize &lt;a href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#inputs" rel="noopener noreferrer"&gt;input parameters&lt;/a&gt; that the user can provide when calling the action in a job step. These parameters are passed to your container as command-line arguments. This is the easiest way to parameterize your action. Your action's descriptor file must define any input parameters that you wish to support, including their name, type, default value, and whether they're required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom GitHub Actions in Rust
&lt;/h2&gt;

&lt;p&gt;If you haven't dipped your touch-typing fingers into Rust yet, you really owe it to yourself. &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; is a modern programming language with features that make it suitable not only for systems programming -- its original purpose, but just about any other environment, too; there are frameworks that let your build web services, &lt;a href="https://www.arewewebyet.org/" rel="noopener noreferrer"&gt;web applications&lt;/a&gt; including user interfaces, software for embedded devices, &lt;a href="https://www.arewelearningyet.com/" rel="noopener noreferrer"&gt;machine learning&lt;/a&gt; solutions, and of course, command-line tools. Since a custom GitHub Action is essentially a command-line tool that interacts with the system through files and environment variables, Rust is perfectly suited for that as well.&lt;/p&gt;

&lt;p&gt;Rust has a &lt;a href="https://crates.io/" rel="noopener noreferrer"&gt;rich ecosystem&lt;/a&gt; of frameworks and libraries that let you read, parse, and manipulate text files, interact with cloud services and databases, and perform any other job that your project's development workflow may require. And because of its strong typing and tight memory management, you are much less likely to write programs that behave unexpectedly in production.&lt;/p&gt;

&lt;p&gt;It is also quite easy to build a Rust program that links all of its dependencies into a single executable file. When you also &lt;a href="https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level" rel="noopener noreferrer"&gt;optimize your release build for size&lt;/a&gt;, you end up with a small Docker container image that is quick to load and takes very little memory to do its job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for building custom GitHub Actions in Rust
&lt;/h2&gt;

&lt;p&gt;As you get started, consider the following tips to help you along:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Use the &lt;code&gt;act&lt;/code&gt; tool to test-run your action
&lt;/h3&gt;

&lt;p&gt;To speed up your development cycle, install and use the &lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;act&lt;/a&gt; tool to test-run your action directly in your development environment. This tool lets you invoke a GitHub workflow right on your local machine and will save you the round-trips of pushing each change to GitHub to see if it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use debug messages
&lt;/h3&gt;

&lt;p&gt;GitHub supports a &lt;a href="https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging" rel="noopener noreferrer"&gt;debug mode&lt;/a&gt; when executing workflows. This will cause any &lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-debug-message" rel="noopener noreferrer"&gt;debug message&lt;/a&gt; output to show up in the action's logs. This feature gives your users the ability to debug their workflows in a uniform manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make your code easily testable
&lt;/h3&gt;

&lt;p&gt;Write lots of unit tests, and use libraries that help you test filesystem operations, such as &lt;a href="https://crates.io/crates/assert_fs" rel="noopener noreferrer"&gt;assert_fs&lt;/a&gt;, as well as your command-line parsing, such as &lt;a href="https://crates.io/crates/assert_cmd" rel="noopener noreferrer"&gt;assert_cmd&lt;/a&gt;. This will save you a lot of headaches with regression bugs as you build out your action's functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Keep command-line argument parsing simple
&lt;/h3&gt;

&lt;p&gt;Fancy command-line argument parsing simply isn't necessary when building a custom GitHub Action. All input parameters are described and fed statically into your executable's command-line arguments. That means that all parameters will always be supplied in the order you define in &lt;code&gt;action.yaml&lt;/code&gt;, regardless of their values, so ensure that your code knows how to handle empty strings and defaults.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Use GitHub Actions to automate your own development process
&lt;/h3&gt;

&lt;p&gt;Not surprisingly, you can invoke your own action as part of your action's automated workflow! In addition to your regular Rust-based project workflow, add one that calls your action with a set of resources that you can use to test its functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Build and publish your Docker image to ghcr.io for free
&lt;/h3&gt;

&lt;p&gt;Take full advantage of what GitHub has to offer, for free:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build and package your action as a Docker container image as part of your release workflow.&lt;/li&gt;
&lt;li&gt;Publish it to GitHub's &lt;a href="https://ghcr.io" rel="noopener noreferrer"&gt;Container Registry&lt;/a&gt;, which you can set up for your action's repository.&lt;/li&gt;
&lt;li&gt;Update your action's descriptor to use the desired version of your pre-built Docker image, which will greatly speed up its execution!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7. Use Alpine-based Rust builder for statically-linked executables
&lt;/h3&gt;

&lt;p&gt;For the smallest possible Docker container images, build your action using an &lt;a href="https://hub.docker.com/_/rust/tags?page=1&amp;amp;name=alpine" rel="noopener noreferrer"&gt;Alpine Linux-based Rust builder&lt;/a&gt;. This image configures your Rust toolchain for static linking using the &lt;a href="https://musl.libc.org/" rel="noopener noreferrer"&gt;musl library&lt;/a&gt;, which produces single-file executables without any dynamic library dependencies. With this approach, your final Docker container image can consist of &lt;a href="https://hub.docker.com/_/scratch/" rel="noopener noreferrer"&gt;your executable alone&lt;/a&gt;, with no other files needed!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;To learn more, sign up for this interactive project on how to &lt;a href="https://www.educative.io/projects/build-custom-github-actions-in-rust" rel="noopener noreferrer"&gt;Build Custom GitHub Actions in Rust&lt;/a&gt; at Educative.io, which will take you step by step through creating your own custom GitHub Action in Rust!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>githubactions</category>
      <category>docker</category>
      <category>cicd</category>
    </item>
    <item>
      <title>The Undistributed System</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Wed, 27 Mar 2024 03:00:19 +0000</pubDate>
      <link>https://dev.to/pnehrer/the-undistributed-system-425h</link>
      <guid>https://dev.to/pnehrer/the-undistributed-system-425h</guid>
      <description>&lt;p&gt;One common yet surprisingly stubborn problem in many evolving software products is the naive assumption that things happen in sequence. This situation is often the result of a noble intent -- don't over-architect, over-engineer the solution to something that seems like a simple problem at the outset. This simplification is often intentional, instituted in order to make the problem more manageable. After all, most systems aren't complex right from the start!&lt;/p&gt;

&lt;p&gt;When a system is initially designed without considering the complexities of distributed computing, it often assumes a single, centralized state management approach. This design assumes consistent, instantaneous access to data, neglects the latency in communication between different parts of the system, and overlooks the possibility of node failures. Such assumptions are viable in a monolithic setup but become problematic when transitioning to a distributed architecture.&lt;/p&gt;

&lt;p&gt;This conundrum isn't specific to globally distributed databases; it can occur in your single-zone Kubernetes cluster, or even a single-die, multi-core processor!&lt;/p&gt;

&lt;p&gt;Time-based causality relies on the assumption that events can be ordered based on timestamps, ostensibly providing a straightforward way to determine the sequence of events in a distributed system. However, this approach is fraught with challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenges
&lt;/h2&gt;

&lt;p&gt;For one, achieving data consistency across distributed nodes is a significant challenge. In a naive system, consistency is often taken for granted, assuming immediate visibility of all changes. However, in a distributed setting, data may be replicated across nodes, leading to potential inconsistencies due to network delays or partitions. Retrofitting consistency mechanisms like distributed consensus protocols can be complex and requires fundamental changes to how data is accessed and updated.&lt;/p&gt;

&lt;p&gt;A non-distributed system may not have robust mechanisms for handling partial failures, as the entire system typically runs in a single process or machine. Distributed systems, by contrast, must be designed to tolerate failures of individual components without compromising the overall system availability. Implementing fault tolerance mechanisms, such as leader election, replication, and data recovery protocols, into a system not originally designed for this can be exceedingly challenging.&lt;/p&gt;

&lt;p&gt;A naive system design often overlooks the impact of network latency and the possibility of network partitions. In distributed systems, these factors can significantly affect performance and consistency. Designing for distributed state use-cases requires careful consideration of these network issues, including strategies for dealing with partitioned networks and ensuring that the system can still function under such conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential Mitigations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" rel="noopener noreferrer"&gt;Conflict-Free Replicated Data Types&lt;/a&gt; (CRDTs) are data structures designed to ensure eventual consistency across distributed systems without the need for centralized coordination. They allow concurrent updates and resolve conflicts in a deterministic manner, ensuring convergence. However, CRDTs come with their trade-offs, including increased complexity in understanding and implementing these data structures, as well as the overhead of resolving conflicts, which can impact performance.&lt;/p&gt;

&lt;p&gt;Distributed Consensus Protocols, such as &lt;a href="https://en.wikipedia.org/wiki/Paxos_(computer_science)" rel="noopener noreferrer"&gt;Paxos&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Raft_(algorithm)" rel="noopener noreferrer"&gt;Raft&lt;/a&gt;, facilitate agreement among distributed processes on a single data value, ensuring consistency and fault tolerance. However, they incur costs in terms of latency due to the need for multiple rounds of communication between nodes to achieve consensus, and they also require a majority of nodes to be operational, limiting scalability in large, highly distributed environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating the Trade-Offs
&lt;/h2&gt;

&lt;p&gt;While CRDTs offer a way to sidestep the issues of time-based causality by enabling eventual consistency and conflict resolution without central coordination, they may not be suitable for applications requiring immediate consistency across all nodes. Distributed consensus protocols, on the other hand, provide strong consistency guarantees but at the expense of increased communication overhead and potential bottlenecks in decision-making processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR;
&lt;/h2&gt;

&lt;p&gt;Transitioning from a naive to a distributed system design is not merely an extension but a paradigm shift. It requires rethinking and often redesigning core components to handle the realities of distributed computing. Early consideration of distributed systems principles can mitigate these challenges, allowing for more seamless scaling and adaptation as requirements evolve. The process of retrofitting a system for distributed use-cases serves as a potent reminder of the complexity of distributed systems and the importance of incorporating distributed systems thinking from the outset of system design.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>systemdesign</category>
      <category>kubernetes</category>
      <category>database</category>
    </item>
    <item>
      <title>Configuring Rust Applications</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Mon, 25 Mar 2024 04:08:52 +0000</pubDate>
      <link>https://dev.to/pnehrer/configuring-rust-applications-2imb</link>
      <guid>https://dev.to/pnehrer/configuring-rust-applications-2imb</guid>
      <description>&lt;p&gt;When building an application in any programming language, managing configuration is a fundamental aspect that determines how flexible and user-friendly your software can be. Among numerous crates available for handling configuration in Rust, two noteworthy options stand out: &lt;a href="https://crates.io/crates/clap" rel="noopener noreferrer"&gt;clap&lt;/a&gt; and &lt;a href="https://crates.io/crates/figment" rel="noopener noreferrer"&gt;figment&lt;/a&gt;. Both serve the purpose of managing application settings, but they cater to slightly different needs and use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're happy (and you know it)
&lt;/h2&gt;

&lt;p&gt;Clap is a powerful solution for parsing command-line arguments in Rust. It is designed to make the process of defining and accessing command-line parameters straightforward, with a focus on ease of use, customization, and comprehensive error handling. Clap allows developers to quickly add command-line parsing to their applications, supporting a wide range of scenarios from simple flag checks to more complex hierarchical subcommands.&lt;/p&gt;

&lt;p&gt;Clap has a macro system that supports defining command-line options declaratively, which can significantly reduce boilerplate code. The crate automatically generates help messages based on the defined command-line arguments, ensuring that users always have access to guidance on how to use the application. Thanks to its robust error handling, clap provides detailed error messages when users input invalid arguments, making it easier for them to correct their mistakes.&lt;/p&gt;

&lt;p&gt;The primary use-case for clap is a command-line tool that performs file operations based on user inputs, where users can specify file paths, operation modes, and other options directly through the CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not just your imagination
&lt;/h2&gt;

&lt;p&gt;On the other hand, figment is a configuration library that is particularly suited for applications deployed in containerized environments, such as Docker. It focuses on sourcing configuration from a variety of formats and locations, such as environment variables, JSON, TOML files, and more. This flexibility makes figment an excellent choice for applications that need to adapt to different deployment environments without requiring code changes.&lt;/p&gt;

&lt;p&gt;With its flexible configuration source providers, figment can pull configuration data from multiple sources, including files, environment variables, and even custom providers, allowing for a versatile setup that can adapt to various deployment scenarios. The crate also supports layered configuration, where settings can be overridden or merged from different sources. This is particularly useful in Docker-based applications, where environment-specific configurations might need to override defaults. Finally, figment supports deserializing configuration into strongly typed Rust structs, providing compile-time checks and reducing runtime errors.&lt;/p&gt;

&lt;p&gt;Figment is best suited for web services deployed in Docker containers, where database URLs, API keys, and other sensitive settings need to be securely managed and easily changed between development, staging, and production environments without altering the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apples, and orange apples
&lt;/h2&gt;

&lt;p&gt;The primary difference between clap and figment lies in their focus areas. While clap is specialized in parsing command-line arguments, making it ideal for CLI tools and applications that require interactive user input, figment is designed with flexible, multi-source configuration in mind, perfect for applications deployed across different environments, especially in containers.&lt;/p&gt;

&lt;p&gt;Despite their differences, clap and figment share common ground in their ability to utilize environment variables as a source of configuration. This feature is particularly beneficial in modern application development, where the flexibility to configure apps dynamically via the environment is essential for adapting to various deployment contexts without changing the code.&lt;/p&gt;

&lt;p&gt;Clap integrates environment variables into its command-line parsing ecosystem, allowing developers to specify that certain command-line options can be also set or overridden by environment variables. This is useful for scenarios where sensitive information, such as API keys or database passwords, should not be passed directly on the command line or for providing defaults that can be overridden in different deployment environments.&lt;/p&gt;

&lt;p&gt;Figment, inherently designed to handle configuration from multiple sources, treats environment variables as a first-class citizen. It allows developers to seamlessly merge or override configurations specified in files or hardcoded into the application with those specified as environment variables. This approach is especially advantageous in containerized applications, where environment variables are a common mechanism for passing configuration into containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  In summary
&lt;/h2&gt;

&lt;p&gt;For command-line argument parsing with rich user interaction, clap offers a comprehensive toolkit. Meanwhile, for Rust applications requiring versatile configuration management across different environments, especially in Docker-based deployments, figment provides an elegant and powerful solution. Both crates are excellent in their domains, and understanding their strengths can help developers leverage the best of Rust's ecosystem for configuration management.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cli</category>
      <category>docker</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>What's your crate's Minimum Supported Rust Version?</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Wed, 04 Jan 2023 04:30:01 +0000</pubDate>
      <link>https://dev.to/pnehrer/whats-your-crates-minimum-supported-rust-version-28mh</link>
      <guid>https://dev.to/pnehrer/whats-your-crates-minimum-supported-rust-version-28mh</guid>
      <description>&lt;p&gt;You worked hard to build your Rust library. You followed best practices, took advantage of those expressive yet concise language constructs, pulled in rock-solid dependencies instead of reinventing the wheel...&lt;/p&gt;

&lt;p&gt;Your library is useful; its API lean and to the point, generic where it makes sense. Even your unit test coverage is decent!&lt;/p&gt;

&lt;p&gt;You're ready to publish it for the world to enjoy.&lt;/p&gt;

&lt;p&gt;But what version of Rust does it really require?&lt;/p&gt;

&lt;p&gt;Maybe you're one of those fearless explorers who live on &lt;em&gt;nightly&lt;/em&gt;. Or maybe you use the latest &lt;em&gt;stable&lt;/em&gt; but keep it up-to-date as soon as a new release comes out so you can take take advantage of those sweet-sweet goodies that just about every new version delivers!&lt;/p&gt;

&lt;p&gt;Before Rust 1.56, you'd have to informally document the version that you &lt;em&gt;thought&lt;/em&gt; was required; for example, the one that you used to build your crate, or retroactively determined by installing older versions of Rust. Your users would then have to follow this advice.&lt;/p&gt;

&lt;p&gt;However, starting with version 1.56 Rust allows crate authors to specify their &lt;a href="https://github.com/rust-lang/rfcs/blob/master/text/2495-min-rust-version.md" rel="noopener noreferrer"&gt;Minimum Supported Rust Version&lt;/a&gt; as &lt;a href="https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field" rel="noopener noreferrer"&gt;rust-version in Cargo.toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nice! Yet the original problem persists -- what version should you enter into that field?&lt;/p&gt;

&lt;p&gt;If you go with the version that you're currently on, then it's possible you're unnecessarily forcing your users to that version or newer.&lt;/p&gt;

&lt;p&gt;On the other hand, constraining yourself to an older version doesn't bring much joy (unless of course you have a specific audience in mind).&lt;/p&gt;

&lt;p&gt;Ready to dive into shell and whip up a for-loop to install every possible Rust version to see which one breaks the build?&lt;/p&gt;

&lt;p&gt;Before getting overcome by despair, have a look at &lt;a href="https://github.com/foresterre/cargo-msrv" rel="noopener noreferrer"&gt;cargo-msrv&lt;/a&gt; -- this little gem of a tool figures it all out for you!&lt;/p&gt;

&lt;p&gt;First, install it like you would any other cargo subcommand; e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo install cargo-msrv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, let it determine which Rust version your crate &lt;em&gt;actually&lt;/em&gt; requires based on your code &lt;em&gt;as well as&lt;/em&gt; your dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo msrv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et violà:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Determining the Minimum Supported Rust Version (MSRV) for toolchain aarch64-apple-darwin
Using check command cargo check
Check for toolchain '1.61.0-aarch64-apple-darwin' succeeded
Check for toolchain '1.58.1-aarch64-apple-darwin' succeeded
Check for toolchain '1.57.0-aarch64-apple-darwin' succeeded
Check for toolchain '1.56.1-aarch64-apple-darwin' succeeded
   Finished The MSRV is: 1.56.1   ██████████████████████████████████████████████████████████████████████████████████████████████████████████ 00:00:45
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only that; it'll also tell you &lt;em&gt;why&lt;/em&gt; an earlier version would break the build! That way, you can decide if this is something that you can change or work around in order to support an older Rust version, thus potentially reaching a wider audience.&lt;/p&gt;

&lt;p&gt;Once you've set your Cargo.toml's &lt;code&gt;rust-version&lt;/code&gt; to the recommended value, you can verify that it still holds as you continue developing your crate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo msrv verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool offers a lot more &lt;a href="https://foresterre.github.io/cargo-msrv/" rel="noopener noreferrer"&gt;helpful features&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Kudos to the &lt;a href="https://github.com/foresterre" rel="noopener noreferrer"&gt;author&lt;/a&gt; and thanks to all those who &lt;a href="https://github.com/foresterre/cargo-msrv/graphs/contributors" rel="noopener noreferrer"&gt;contributed&lt;/a&gt;; I was delighted to have discovered this very useful tool!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cargo</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Story of Rusty Containers, Queues, and the Role of Assumed Identity</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Wed, 26 Aug 2020 00:43:07 +0000</pubDate>
      <link>https://dev.to/pnehrer/a-story-of-rusty-containers-queues-and-the-role-of-assumed-identity-kl2</link>
      <guid>https://dev.to/pnehrer/a-story-of-rusty-containers-queues-and-the-role-of-assumed-identity-kl2</guid>
      <description>&lt;h1&gt;
  
  
  How to access Amazon Web Services from Rust-based Kubernetes applications using Rusoto and IAM Roles for Service Accounts
&lt;/h1&gt;

&lt;p&gt;I made acquaintance with &lt;a href="https://github.com/rusoto/rusoto" rel="noopener noreferrer"&gt;Rusoto&lt;/a&gt; only recently, while building a Rust service that consumed messages from Amazon SQS. I was instantly impressed with this &lt;em&gt;AWS SDK for Rust&lt;/em&gt; -- well designed, modular, thoroughly documented, and even more comprehensive than typical AWS SDKs for other languages.&lt;/p&gt;

&lt;p&gt;In order to set up my application to use SQS, all I had to do was add &lt;a href="https://crates.io/crates/rusoto_core" rel="noopener noreferrer"&gt;rusoto_core&lt;/a&gt; and &lt;a href="https://crates.io/crates/rusoto_sqs" rel="noopener noreferrer"&gt;rusoto_sqs&lt;/a&gt; crates to my &lt;code&gt;Cargo.toml&lt;/code&gt; and create an instance of &lt;a href="https://docs.rs/rusoto_sqs/0.45.0/rusoto_sqs/struct.SqsClient.html" rel="noopener noreferrer"&gt;SqsClient&lt;/a&gt; for my target region. Even better, I could set it up with the &lt;a href="https://docs.rs/rusoto_core/0.45.0/rusoto_core/enum.Region.html#default" rel="noopener noreferrer"&gt;default region&lt;/a&gt;, which causes it to look for the configured region in the usual environment variables or profile configuration files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_sqs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my containerized application this was ideal, since in Kubernetes it would get its static configuration through environment variables anyway, and I could easily supply them when running locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug &lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ca-central-1 &lt;span class="nv"&gt;QUEUE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://sqs.ca-central-1.amazonaws.com/1234567890/rusoto-sqs-k8s-demo cargo run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Supplying AWS credentials
&lt;/h2&gt;

&lt;p&gt;Similar to how it determines the desired service region, the default Rusoto client uses a &lt;a href="https://rusoto.github.io/rusoto/rusoto_credential/struct.ChainProvider.html" rel="noopener noreferrer"&gt;discovery algorithm&lt;/a&gt; to obtain its AWS credentials; it checks the environment variables, profile configuration files, and even the IAM instance profile if running on an EC2 instance.&lt;/p&gt;

&lt;p&gt;If any of the supplied credentials are configured to expire periodically, this provider would even refresh them as needed!&lt;/p&gt;

&lt;p&gt;As expected, running my code locally was a breeze, since the client used my default AWS profile credentials, which were granted access to the SQS queue that I had set up for testing. I tried sending a test message to my queue using AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sqs send-message &lt;span class="nt"&gt;--queue-url&lt;/span&gt; https://sqs.ca-central-1.amazonaws.com/1234567890/rusoto-sqs-k8s-demo &lt;span class="nt"&gt;--message-body&lt;/span&gt; &lt;span class="s1"&gt;'Hello world!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! As my application's debug log indicated, it was able to receive and process the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Aug 23 23:58:09.780"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Message { attributes: None, body: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello world!&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), md5_of_body: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;86fb269d190d2c85f6e0468ceca42a20&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), md5_of_message_attributes: None, message_attributes: None, message_id: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;d1ec1019-6398-4c75-b320-4a1e653e63ef&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), receipt_handle: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;AQEBDrxJ...fnjddGjP8J6zvFKtw==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;) }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"log.target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"log.module_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"log.file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"src/main.rs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"log.line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;129&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In order to demonstrate the approach outlined in this article, I created a small &lt;a href="https://github.com/ecliptical/rusoto-sqs-k8s-demo" rel="noopener noreferrer"&gt;demonstration project&lt;/a&gt; that's available on GitHub. Most of the code snippets and log outputs in this article are taken from this project. Please note that it is very minimalistic, focusing on the subject at hand and glossing over other important aspects, such as error handling, testing, and deployment. It could even use a more efficient Docker build! Perhaps more on that in another article...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Detour: Kubernetizing your application
&lt;/h2&gt;

&lt;p&gt;Applications running in Kubernetes differ from typical CLI-based programs in a number of ways. For instance, they have different configuration and logging needs. Furthermore, they must support readiness and liveness probes, and gracefully shut down in response to the TERM signal. Finally, they must run in a Docker container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;Granted, log entries like the one shown earlier aren't as human-readable as those generated by e.g., the &lt;a href="https://crates.io/crates/pretty_env_logger" rel="noopener noreferrer"&gt;pretty_env_logger&lt;/a&gt; crate; however, because they are JSON, they are easily consumable by various application monitoring tools.&lt;/p&gt;

&lt;p&gt;For Kubernetes applications that use &lt;a href="https://crates.io/crates/tokio" rel="noopener noreferrer"&gt;tokio&lt;/a&gt; to implement asynchronous servers I typically reach for the &lt;a href="https://crates.io/crates/tracing" rel="noopener noreferrer"&gt;tracing&lt;/a&gt; and &lt;a href="https://crates.io/crates/tracing-subscriber" rel="noopener noreferrer"&gt;tracing-subscriber&lt;/a&gt; crates. This pair gives me the ability to use the &lt;a href="https://crates.io/crates/log" rel="noopener noreferrer"&gt;log&lt;/a&gt; crate as I normally would -- for logging -- as well as async-aware tracing, should the need arise. As a bonus, it provides JSON-formatted log output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tracing_subscriber&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Subscriber&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;TracingSubscriber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EnvFilter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;TracingEnvFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nn"&gt;TracingSubscriber&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.with_env_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TracingEnvFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_default_env&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;For better user experience with a CLI-based application, I would normally utilize a crate such as &lt;a href="https://crates.io/crates/gumdrop" rel="noopener noreferrer"&gt;gumdrop&lt;/a&gt; in order to support argument-based configuration. However, there is no need for that in Kubernetes as the application primarily receives its configuration through environment variables passed down to it through Docker, config maps mounted as files on filesystem volumes, or dynamically through an external service (e.g., Consul).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://crates.io/crates/config" rel="noopener noreferrer"&gt;config&lt;/a&gt; create does the trick here -- it supports these types of configuration sources while allowing me to read configuration values into a typed struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;config&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[serde(default&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Settings::default_status_probe_addr"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;status_probe_addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default_status_probe_addr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"0.0.0.0:8080"&lt;/span&gt;
            &lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"default status probe address"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="nf"&gt;.merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build info
&lt;/h3&gt;

&lt;p&gt;For easier debugging, I like to have the application log its current version at startup. This can be easily accomplished with the help of the &lt;a href="https://crates.io/crates/built" rel="noopener noreferrer"&gt;built&lt;/a&gt; crate, which projects various build-time data, such as the current git hash, into constants, which can in turn be used to create the version string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;version&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"{} {} ({}, {} build, {} [{}], {})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO_PKG_NAME"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nd"&gt;env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO_PKG_VERSION"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;built_info&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GIT_VERSION&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unknown"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;built_info&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PROFILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;built_info&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CFG_OS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;built_info&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CFG_TARGET_ARCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;built_info&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BUILT_TIME_UTC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Readiness and liveness probes
&lt;/h3&gt;

&lt;p&gt;Kubernetes must be able to determine the health of each container in order to replace it in case the application runs into some unexpected trouble. To do that, the container can be configured with a readiness (checked upon startup) and a liveness probe (checked periodically), which tells Kubernetes when the container is ready to receive traffic as well as alive and healthy, respectively.&lt;/p&gt;

&lt;p&gt;For the demo application, I chose a simple TCP connection probe -- as long as the application accepts the controller's connection request on the specified port, the probe is deemed successful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;status_listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.status_probe_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;probes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status_listener&lt;/span&gt;&lt;span class="nf"&gt;.incoming&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;probes&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Signals
&lt;/h3&gt;

&lt;p&gt;It's a good practice to have your application handle POSIX signals, especially the TERM signal, which the container host sends to the application upon graceful shutdown. The application should use that opportunity to finish processing any outstanding requests and clean up any open resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SelectAll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SignalKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SelectAll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SignalKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to register the interrupt signal"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SignalKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to register the quit signal"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SignalKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to register the terminate signal"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// ignore SIGPIPE&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_sigpipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SignalKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to register the pipe signal"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Clean up and exit&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker build
&lt;/h3&gt;

&lt;p&gt;In order to run in Kubernetes, the application must be packaged as a Docker image. One of the easiest way to accomplish this is to create a multi-stage &lt;code&gt;Dockerfile&lt;/code&gt; using &lt;a href="https://github.com/emk/rust-musl-builder" rel="noopener noreferrer"&gt;Rust MUSL Builder&lt;/a&gt; to build the application and &lt;a href="https://www.alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux&lt;/a&gt; as the base of the target image.&lt;/p&gt;

&lt;p&gt;Rust MUSL Builder comes pre-installed with the desired Rust toolchain. Furthermore, it produces statically-linked builds, which comes in handy when running the binaries in Alpine Linux.&lt;/p&gt;

&lt;p&gt;After verifying the formatting, running clippy, unit tests, and finally the release build (with the desired maximum logging level), the target binary is copied into Alpine Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;cargo &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo clippy &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; warnings
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; debug&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUILD_FEATURES=${debug:+"--features log-level-trace"}&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--no-default-features&lt;/span&gt; &lt;span class="nv"&gt;$BUILD_FEATURES&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, for better process handling, I recommend installing &lt;a href="https://github.com/krallin/tini" rel="noopener noreferrer"&gt;tini&lt;/a&gt; and having it spawn the actual application process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apk &lt;span class="nt"&gt;--no-cache&lt;/span&gt; add tini
...
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/sbin/tini", "--"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/app/rusoto-sqs-k8s-demo"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, running the build takes a good while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; ecliptical/rusoto-sqs-k8s-demo &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  140.3kB
Step 1/17 : FROM ekidd/rust-musl-builder:nightly-2020-08-15 AS build
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; c39cf12c752f
 ...
  &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ea152bab848a
Successfully built ea152bab848a
Successfully tagged ecliptical/rusoto-sqs-k8s-demo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying into Kubernetes
&lt;/h2&gt;

&lt;p&gt;After the resounding success of the application's trial run on my laptop, I was ready to press ahead and deploy it into Kubernetes!&lt;/p&gt;

&lt;p&gt;Your organization's approach for deploying applications into Kubernetes will likely vary. For instance, one might use the Amazon Elastic Container Registry (ECR) to host the Docker images, and &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; to simplify the various deployment descriptors and the actual roll-out procedures. However, for demonstration purposes a &lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;GitHub Package Registry&lt;/a&gt; and plain Kubernetes deployment descriptors applied using &lt;code&gt;kubectl&lt;/code&gt; will suffice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker registry
&lt;/h3&gt;

&lt;p&gt;To allow Kubernetes to deploy a Docker container, it must be able to download the specified image from a Docker registry. For the demo project, I set up a GitHub Package Registry at &lt;code&gt;docker.pkg.github.com/ecliptical/rusoto-sqs-k8s-demo&lt;/code&gt; and pushed the tagged build into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag ecliptical/rusoto-sqs-k8s-demo docker.pkg.github.com/ecliptical/rusoto-sqs-k8s-demo/rusoto-sqs-k8s-demo:v1
docker push docker.pkg.github.com/ecliptical/rusoto-sqs-k8s-demo/rusoto-sqs-k8s-demo:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, your organization's CI/CD system takes care of running the build and pushing it to the Docker registry!&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment descriptor
&lt;/h3&gt;

&lt;p&gt;In order to deploy the application into Kubernetes, &lt;code&gt;kubectl&lt;/code&gt; needs a &lt;code&gt;deployment.yaml&lt;/code&gt; file that describes the various aspects of the deployment, including the containers and their configuration. This is where you supply static values for the various environment variables either directly or as secrets. In practice, these are often managed by the SRE team or other authorized personnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker.pkg.github.com/ecliptical/rusoto-sqs-k8s-demo/rusoto-sqs-k8s-demo:v1"&lt;/span&gt;
  &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_REGION"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca-central-1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_ACCESS_KEY_ID"&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws_access_key_id&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_SECRET_ACCCESS_KEY"&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws_secret_access_key&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QUEUE_URL"&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;queue_url&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RUST_LOG"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;regsecret&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;For the above deployment descriptor to work, there must a be a special &lt;code&gt;regsecret&lt;/code&gt; and a generic &lt;code&gt;rusoto-sqs-k8s-demo-secrets&lt;/code&gt; secret set up in the target namespace. For the demo, you can create the former like so (after replacing your GitHub credentials):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AUTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; YOUR_GITHUB_USERNAME:YOUR_GITHUB_API_TOKEN | &lt;span class="nb"&gt;base64&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"auths":{"docker.pkg.github.com":{"auth":"'&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AUTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'"}}}'&lt;/span&gt; | kubectl create secret &lt;span class="nt"&gt;-n&lt;/span&gt; demo generic regsecret &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kubernetes.io/dockerconfigjson &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.dockerconfigjson&lt;span class="o"&gt;=&lt;/span&gt;/dev/stdin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the latter (partially):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; demo create secret generic rusoto-sqs-k8s-demo-secrets &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;queue_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://sqs.ca-central-1.amazonaws.com/1234567890/rusoto-sqs-k8s-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the actual application I asked the Operations Team to kindly set up the registry secret and issue a new set of AWS credentials for my new-fangled application with relevant permissions for the SQS queue in question...&lt;/p&gt;

&lt;h2&gt;
  
  
  Not so fast!
&lt;/h2&gt;

&lt;p&gt;It turns out that issuing static AWS credentials (that is, the &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; pair) to Kubernetes applications isn't a great idea! The preferred way is to have the application assume a designated IAM role, which can then be granted various permissions as needed. In contrast to access-key based credentials, which are issued to a user, IAM roles may be scoped specifically to the set of permissions that the application needs, thus improving your system's security posture through the principle of least privilege.&lt;/p&gt;

&lt;p&gt;For "classic" applications running in EC2, this can be done through IAM instance profiles.&lt;/p&gt;

&lt;p&gt;But what about Kubernetes?&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine-grained IAM Roles for Service Accounts
&lt;/h2&gt;

&lt;p&gt;Luckily, Kubernetes applications can take advantage of &lt;a href="https://aws.amazon.com/id/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/" rel="noopener noreferrer"&gt;fine-grained IAM roles for service accounts&lt;/a&gt;. This approach combines Kubernetes' Role-Based Access Control (RBAC) with Amazon's Identity and Access Management (IAM). The details of this mechanism are somewhat involved -- it takes advantage of the fact that Kubernetes can issue &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection" rel="noopener noreferrer"&gt;projected service account tokens&lt;/a&gt; for pods. Since these are valid OIDC JWTs, Amazon's Secure Token Service (STS) can use them for authentication thanks to its support for &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html" rel="noopener noreferrer"&gt;OIDC federation&lt;/a&gt;. Thus, a Kubernetes pod with a specific service account may be linked to an IAM role through STS. This ends up being relatively straightforward particularly in Amazon's Elastic Kubernetes Service (EKS), since its control plane takes care of automatically provisioning, injecting, and periodically updating the necessary environment variables and projected filesystem volume. However, the same web-hook based mechanism can be implemented in other environments.&lt;/p&gt;

&lt;p&gt;The bottom line -- with a little bit of additional Kubernetes configuration, my application can get access to automatically managed, periodically refreshed AWS credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating IAM role
&lt;/h3&gt;

&lt;p&gt;Your organization's policies will likely determine how to go about provisioning an IAM role for your application. For this demo, you can just use the AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-policy &lt;span class="nt"&gt;--policy-name&lt;/span&gt; RusotoSQSK8sDemoConsumer &lt;span class="nt"&gt;--policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": ["sqs:DeleteMessage", "sqs:GetQueueUrl", "sqs:ChangeMessageVisibility", "sqs:DeleteMessageBatch", "sqs:ReceiveMessage", "sqs:GetQueueAttributes", "sqs:ChangeMessageVisibilityBatch"], "Resource": ["arn:aws:sqs:*:1234567890:*"]}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;RusotoSQSK8sDemoConsumer&lt;/code&gt; role with enough permissions to receive and process messages in any SQS queue in the given account.&lt;/p&gt;

&lt;h3&gt;
  
  
  OIDC provider setup
&lt;/h3&gt;

&lt;p&gt;Depending on your particular environment, the steps for setting up an Open ID Connect provider for your Kubernetes cluster will vary. If using EKS, you can create the provider and configure your cluster to use it in one step with the help of &lt;a href="https://eksctl.io/" rel="noopener noreferrer"&gt;eksctl&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl utils associate-iam-oidc-provider &lt;span class="nt"&gt;--cluster&lt;/span&gt; rusoto-sqs-demo &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  eksctl version 0.26.0
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  using region ca-central-1
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  will create IAM Open ID Connect provider &lt;span class="k"&gt;for &lt;/span&gt;cluster &lt;span class="s2"&gt;"rusoto-sqs-demo"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"ca-central-1"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;✔]  created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kubernetes service account
&lt;/h3&gt;

&lt;p&gt;Next, you need a Kubernetes service account annotated with the ARN of the previously provisioned IAM role, which you can then assign to your application pods.&lt;/p&gt;

&lt;p&gt;Here again, &lt;code&gt;eksctl&lt;/code&gt; makes this process a one-step operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--name&lt;/span&gt; rusoto-sqs-consumer &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--namespace&lt;/span&gt; demo &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--cluster&lt;/span&gt; rusoto-sqs-demo &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt; arn:aws:iam::123456789:policy/RusotoSQSK8sDemoConsumer &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  eksctl version 0.26.0
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  using region ca-central-1
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  1 task: &lt;span class="o"&gt;{&lt;/span&gt; 2 sequential sub-tasks: &lt;span class="o"&gt;{&lt;/span&gt; create IAM role &lt;span class="k"&gt;for &lt;/span&gt;serviceaccount &lt;span class="s2"&gt;"demo/rusoto-sqs-consumer"&lt;/span&gt;, create serviceaccount &lt;span class="s2"&gt;"demo/rusoto-sqs-consumer"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  building iamserviceaccount stack &lt;span class="s2"&gt;"eksctl-rusoto-sqs-demo-addon-iamserviceaccount-demo-rusoto-sqs-consumer"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ℹ]  deploying stack &lt;span class="s2"&gt;"eksctl-rusoto-sqs-demo-addon-iamserviceaccount-demo-rusoto-sqs-consumer"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;✔]  created serviceaccount &lt;span class="s2"&gt;"demo/rusoto-sqs-consumer"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment descriptor changes
&lt;/h3&gt;

&lt;p&gt;Finally, you can incorporate the required changes into your pods' deployment descriptor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_REGION"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca-central-1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QUEUE_URL"&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;queue_url&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RUST_LOG"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-consumer&lt;/span&gt;
&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;65534&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two sets of changes stand out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You no longer need the &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; environment variables -- they've been removed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;serviceAccountName&lt;/code&gt; property has been added specifying the desired service account name, and a &lt;code&gt;securityContext&lt;/code&gt; section with the &lt;code&gt;fsGroup&lt;/code&gt; property -- this instructs Kubernetes to mount filesystem volumes with user &lt;code&gt;nobody&lt;/code&gt; as their owner (id 65534 in Linux), thus allowing the unprivileged application process to read the automatically injected web token file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All in all, when the pod is deployed, the EKS control plane will automatically inject two new environment variables and mount a new filesystem volume containing the periodically refreshed OIDC JWT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rusoto-sqs-k8s-demo&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS_ROLE_ARN&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::1234567890:role/eksctl-rusoto-sqs-demo-addon-iamserviceaccou-Role1-1UNI8CG3YVFKN&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS_WEB_IDENTITY_TOKEN_FILE&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/secrets/eks.amazonaws.com/serviceaccount/token&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/secrets/eks.amazonaws.com/serviceaccount&lt;/span&gt;
      &lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-iam-token&lt;/span&gt;
      &lt;span class="s"&gt;readOnly&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-iam-token&lt;/span&gt;
    &lt;span class="na"&gt;projected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;420&lt;/span&gt;
      &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceAccountToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts.amazonaws.com&lt;/span&gt;
          &lt;span class="na"&gt;expirationSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rusoto credentials, revisited
&lt;/h2&gt;

&lt;p&gt;One outstanding issue remained -- how is the application going to find and use these injected credentials?&lt;/p&gt;

&lt;p&gt;Rusoto is quite flexible -- the &lt;a href="https://crates.io/crates/rusoto_credential" rel="noopener noreferrer"&gt;rusoto_credential&lt;/a&gt; crate allows me to implement my own credential providers, which could obtain and refresh my application's AWS credentials from an arbitrary external source. With this in mind I set out to investigate what it would take to read the injected &lt;code&gt;AWS_WEB_IDENTITY_TOKEN_FILE&lt;/code&gt; and call STS's &lt;code&gt;AssumeRoleWithWebIdentity&lt;/code&gt; to exchange the OIDC JWT for AWS role credentials. Whew!&lt;/p&gt;

&lt;p&gt;Invariably, the trail of breadcrumbs lead me to the &lt;a href="https://crates.io/crates/rusoto_sts" rel="noopener noreferrer"&gt;rusoto_sts&lt;/a&gt; crate, which exposes the Amazon STS API. As I said before, Rusoto is modular and doesn't force you to package code that you won't need. After perusing the documentation for a bit in order to devise my plan of attack, there -- sitting unassumingly at the end of the list of exported structs -- I found the &lt;a href="https://docs.rs/rusoto_sts/0.45.0/rusoto_sts/struct.WebIdentityProvider.html" rel="noopener noreferrer"&gt;WebIdentityProvider&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I couldn't believe my luck!&lt;/p&gt;

&lt;p&gt;With a &lt;a href="https://docs.rs/rusoto_sts/0.45.0/rusoto_sts/struct.WebIdentityProvider.html#method.from_k8s_env" rel="noopener noreferrer"&gt;single method call&lt;/a&gt; I could simply instantiate a different kind of credentials provider that would read the injected environment variables and files and make the appropriate STS calls to authenticate my AWS API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_credential&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AutoRefreshingProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_sqs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_sts&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WebIdentityProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sqs_http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cred_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AutoRefreshingProvider&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;WebIdentityProvider&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_k8s_env&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqs_http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aws_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Onward into Kubernetes, this time for real
&lt;/h2&gt;

&lt;p&gt;With all required changes finally in place, and the Docker image rebuilt and pushed into the registry, I was finally able to deploy the application. For the demo application, the equivalent procedure would simply be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
deployment.apps/rusoto-sqs-k8s-demo created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After receiving similarly positive yet rather anti-climactic output, I tailed the pods' logs to see if another test message sent to the target SQS queue would be picked up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; demo logs &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;rusoto-sqs-k8s-demo
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"Aug 23 23:57:51.508"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"fields"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto-sqs-k8s-demo 0.1.0 (4b04653, release build, linux [x86_64], Sun, 23 Aug 2020 19:12:11 +0000)"&lt;/span&gt;,&lt;span class="s2"&gt;"log.target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.module_path"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.file"&lt;/span&gt;:&lt;span class="s2"&gt;"src/main.rs"&lt;/span&gt;,&lt;span class="s2"&gt;"log.line"&lt;/span&gt;:189&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"Aug 23 23:57:51.488"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"fields"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto-sqs-k8s-demo 0.1.0 (4b04653, release build, linux [x86_64], Sun, 23 Aug 2020 19:12:11 +0000)"&lt;/span&gt;,&lt;span class="s2"&gt;"log.target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.module_path"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.file"&lt;/span&gt;:&lt;span class="s2"&gt;"src/main.rs"&lt;/span&gt;,&lt;span class="s2"&gt;"log.line"&lt;/span&gt;:189&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"Aug 23 23:57:51.690"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"fields"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto-sqs-k8s-demo 0.1.0 (4b04653, release build, linux [x86_64], Sun, 23 Aug 2020 19:12:11 +0000)"&lt;/span&gt;,&lt;span class="s2"&gt;"log.target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.module_path"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.file"&lt;/span&gt;:&lt;span class="s2"&gt;"src/main.rs"&lt;/span&gt;,&lt;span class="s2"&gt;"log.line"&lt;/span&gt;:189&lt;span class="o"&gt;}}&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"Aug 23 23:58:09.780"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"fields"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"Message { attributes: None, body: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello world!&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), md5_of_body: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;86fb269d190d2c85f6e0468ceca42a20&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), md5_of_message_attributes: None, message_attributes: None, message_id: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;d1ec1109-6398-4c75-b032-4a1e6536e3ef&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;), receipt_handle: Some(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;AQEBDwfG...fnjddGjP8J6zvFKtw==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;) }"&lt;/span&gt;,&lt;span class="s2"&gt;"log.target"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.module_path"&lt;/span&gt;:&lt;span class="s2"&gt;"rusoto_sqs_k8s_demo"&lt;/span&gt;,&lt;span class="s2"&gt;"log.file"&lt;/span&gt;:&lt;span class="s2"&gt;"src/main.rs"&lt;/span&gt;,&lt;span class="s2"&gt;"log.line"&lt;/span&gt;:129&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet victory!&lt;/p&gt;

&lt;h2&gt;
  
  
  Have your token-based identity and eat it, too
&lt;/h2&gt;

&lt;p&gt;Ultimately, I decided to support both injected and static AWS credentials in order to make it easier to run the app locally. If the injected credentials are available, then let's use those:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;AwsClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_credential&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AutoRefreshingProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_sqs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rusoto_sts&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WebIdentityProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;var_os&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;var_os&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_WEB_IDENTITY_TOKEN_FILE"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;var_os&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_ROLE_ARN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;aws_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token_file&lt;/span&gt;&lt;span class="nf"&gt;.map_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="nf"&gt;.map_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;AwsClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sqs_http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cred_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AutoRefreshingProvider&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;WebIdentityProvider&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_k8s_env&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nn"&gt;AwsClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cred_provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqs_http&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SqsClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aws_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Even with Kubernetes at the helm, navigating the cloudy skies can become an arduous endeavor, especially when faced with complex, sensitive issues such as application security and controlling access to AWS resources.&lt;/p&gt;

&lt;p&gt;Thankfully, Rusoto provides an easy way to tap into Amazon's support for IAM roles for Kubernetes service accounts, which bridge the Kubernetes and AWS worlds in terms of security and access control.&lt;/p&gt;

&lt;p&gt;I am grateful for and continue to be amazed by all the generous contributions of the open-source Rust community, which make it possible for me and many others to build applications using this powerful, modern language platform.&lt;/p&gt;

&lt;p&gt;To follow the examples in this article please see the &lt;a href="https://github.com/ecliptical/rusoto-sqs-k8s-demo" rel="noopener noreferrer"&gt;accompanying project on GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Splash photo by &lt;a href="https://unsplash.com/@nessa_rin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Rinson Chory&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>kubernetes</category>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>A Curious Tale of Rust TLS and Postgres in the Cloud</title>
      <dc:creator>Peter Nehrer</dc:creator>
      <pubDate>Mon, 03 Aug 2020 15:54:27 +0000</pubDate>
      <link>https://dev.to/pnehrer/a-curious-tale-of-rust-tls-and-postgres-in-the-cloud-434k</link>
      <guid>https://dev.to/pnehrer/a-curious-tale-of-rust-tls-and-postgres-in-the-cloud-434k</guid>
      <description>&lt;h1&gt;
  
  
  How to Connect Securely to Amazon RDS for PostgreSQL using Tokio and Rustls
&lt;/h1&gt;

&lt;p&gt;Recently I ran into a curious problem while working on a piece of asynchronous database code for a micro-service written in Rust -- it looked something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 DEBUG rustls::client::tls12          &amp;gt; Server DNS name is DNSName("database-1.xq7f5vzbpq1x.ca-central-1.rds.amazonaws.com")
 WARN  rustls::session                &amp;gt; Sending fatal alert BadCertificate
Error: Backend(Error { kind: Tls, cause: Some(Kind(InvalidInput)) })


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This particular module used &lt;a href="https://crates.io/crates/tokio-postgres" rel="noopener noreferrer"&gt;Tokio Postgres&lt;/a&gt; along with &lt;a href="https://crates.io/crates/deadpool-postgres" rel="noopener noreferrer"&gt;Deadpool Postgres&lt;/a&gt; -- part of my favorite Rust stack -- to interact with an AWS RDS for PostgreSQL database. Of course, everything worked great when I ran it against a Postgres instance deployed in my local Docker container -- the Deadpool documentation contains &lt;a href="https://docs.rs/deadpool-postgres/0.5.6/deadpool_postgres/index.html#example-with-config-and-dotenv-crate" rel="noopener noreferrer"&gt;easy-to-follow examples&lt;/a&gt; that can get you going in minutes.&lt;/p&gt;

&lt;p&gt;I knew I’d have to support connecting to the database securely in order to deploy the code to production -- the deployment policy requires (as it should!) that all connections to RDS instances use TLS. By default, the Tokio Postgres crate supports plain-text connections to help you get started, but luckily also outlines &lt;a href="https://docs.rs/tokio-postgres/0.5.5/tokio_postgres/index.html#ssltls-support" rel="noopener noreferrer"&gt;steps to enable TLS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To be honest, I was a bit crestfallen when I didn’t see &lt;a href="https://crates.io/crates/rustls" rel="noopener noreferrer"&gt;Rustls&lt;/a&gt; listed as one of the supported methods; only (effectively) OpenSSL. After all, there are &lt;a href="https://www.zdnet.com/article/a-rust-based-tls-library-outperformed-openssl-in-almost-every-category/" rel="noopener noreferrer"&gt;many good reasons&lt;/a&gt; to use Rustls, in addition to the fact that it was already part of my stack (e.g., both &lt;a href="https://crates.io/crates/reqwest" rel="noopener noreferrer"&gt;Reqwest&lt;/a&gt; and &lt;a href="https://crates.io/crates/warp" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; support it) and would allow me to crank out &lt;a href="https://github.com/emk/rust-musl-builder" rel="noopener noreferrer"&gt;statically linked binaries&lt;/a&gt; more easily.&lt;/p&gt;

&lt;p&gt;Never fear -- the amazing Rust community has your back! A quick search through &lt;a href="https://crates.io/" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt; produced the &lt;a href="https://crates.io/crates/tokio-postgres-rustls" rel="noopener noreferrer"&gt;tokio-postgres-rustls&lt;/a&gt; crate, which was exactly what I needed; after a small addition to my configuration and database connection pool setup, I was on my way:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;config&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;deadpool_postgres&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;PoolConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rustls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ClientConfig&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;RustlsClientConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio_postgres&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NoTls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio_postgres_rustls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MakeRustlsConnect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PoolConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[serde(default)]&lt;/span&gt;
    &lt;span class="n"&gt;use_tls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="nf"&gt;.merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.use_tls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tls_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RustlsClientConfig&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MakeRustlsConnect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.pg&lt;/span&gt;&lt;span class="nf"&gt;.create_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.pg&lt;/span&gt;&lt;span class="nf"&gt;.create_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NoTls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Reality Bites
&lt;/h2&gt;

&lt;p&gt;As luck would have it, after pointing my Postgres client configuration to the RDS instance, I was met with the unpleasant surprise I had mentioned earlier -— &lt;em&gt;BadCertificate&lt;/em&gt; error! Wha… wha… whad’ya MEAN??&lt;/p&gt;

&lt;p&gt;I must admit -- in all my excitement I only skimmed through tokio-postgres-rustls documentation, and didn’t even peek at rustls, since the basic configuration example happened to compile just fine (yikes!). After a stern mental self-admonishment I carefully scanned Rustls’s &lt;a href="https://docs.rs/rustls/0.18.0/rustls/index.html#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; example and sure enough, I discovered that I may have missed a step:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="py"&gt;.root_store&lt;/span&gt;&lt;span class="nf"&gt;.add_server_trust_anchors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;webpki_roots&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TLS_SERVER_ROOTS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Of course! Rustls (probably) doesn’t come with any pre-configured root certificates! That would make sense -— Rust libraries tend to defer use-case-specific decisions to the user -- why include files/bytes that may not even be needed?&lt;/p&gt;

&lt;h2&gt;
  
  
  Take Two
&lt;/h2&gt;

&lt;p&gt;Ok -- new dependency included, Mozilla’s trusted root certificates added. That must have been the problem! Alas, the same error:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 DEBUG rustls::client::tls12          &amp;gt; Server DNS name is DNSName("database-1.xq7f5vzbpq1x.ca-central-1.rds.amazonaws.com")
 WARN  rustls::session                &amp;gt; Sending fatal alert BadCertificate
Error: Backend(Error { kind: Tls, cause: Some(Kind(InvalidInput)) })


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;“This means war!”, I thought to myself. Rather, it meant that I had to read the documentation more carefully -— a little bit of sleuthing revealed that AWS utilized their own/separate &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html" rel="noopener noreferrer"&gt;root certificates&lt;/a&gt; for their RDS instances. This, it turns out, is also helpfully indicated in the database instance details in RDS console -— if you know what to look for!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcufzq0ed0eb6hfuznc29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcufzq0ed0eb6hfuznc29.png" alt="Clipping of the Connectivity &amp;amp; security section"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Time’s a Charm
&lt;/h2&gt;

&lt;p&gt;After downloading the &lt;a href="https://s3.amazonaws.com/rds-downloads/rds-ca-2019-root.pem" rel="noopener noreferrer"&gt;requisite root certificate&lt;/a&gt; and adding it to my database pool’s TLS configuration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;config&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;deadpool_postgres&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;PoolConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rustls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ClientConfig&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;RustlsClientConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio_postgres&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NoTls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio_postgres_rustls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MakeRustlsConnect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PoolConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;db_ca_cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="nf"&gt;.merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ca_cert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.db_ca_cert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tls_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RustlsClientConfig&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ca_cert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="py"&gt;.root_store&lt;/span&gt;&lt;span class="nf"&gt;.add_pem_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;anyhow!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to read database root certificate: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca_cert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MakeRustlsConnect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.pg&lt;/span&gt;&lt;span class="nf"&gt;.create_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.pg&lt;/span&gt;&lt;span class="nf"&gt;.create_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NoTls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Success!&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;DEBUG rustls::anchors                &amp;gt; add_pem_file processed 1 valid and 0 invalid certs&lt;br&gt;
 DEBUG rustls::client::hs             &amp;gt; No cached session for DNSNameRef("database-1.xq7f5vzbpq1x.ca-central-1.rds.amazonaws.com")&lt;br&gt;
 DEBUG rustls::client::hs             &amp;gt; Not resuming any session&lt;br&gt;
 DEBUG rustls::client::hs             &amp;gt; ALPN protocol is None&lt;br&gt;
 DEBUG rustls::client::hs             &amp;gt; Using ciphersuite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; ECDHE curve is ECParameters { curve_type: NamedCurve, named_group: secp256r1 }&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; Got CertificateRequest CertificateRequestPayload { certtypes: [RSASign, DSSSign, ECDSASign], sigschemes: [RSA_PKCS1_SHA512, Unknown(1538), ECDSA_NISTP521_SHA512, RSA_PKCS1_SHA384, Unknown(1282), ECDSA_NISTP384_SHA384, RSA_PKCS1_SHA256, Unknown(1026), ECDSA_NISTP256_SHA256, Unknown(769), Unknown(770), Unknown(771), RSA_PKCS1_SHA1, Unknown(514), ECDSA_SHA1_Legacy], canames: [PayloadU16([48, 129, 151, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 87, 97, 115, 104, 105, 110, 103, 116, 111, 110, 49, 16, 48, 14, 6, 3, 85, 4, 7, 12, 7, 83, 101, 97, 116, 116, 108, 101, 49, 34, 48, 32, 6, 3, 85, 4, 10, 12, 25, 65, 109, 97, 122, 111, 110, 32, 87, 101, 98, 32, 83, 101, 114, 118, 105, 99, 101, 115, 44, 32, 73, 110, 99, 46, 49, 19, 48, 17, 6, 3, 85, 4, 11, 12, 10, 65, 109, 97, 122, 111, 110, 32, 82, 68, 83, 49, 40, 48, 38, 6, 3, 85, 4, 3, 12, 31, 65, 109, 97, 122, 111, 110, 32, 82, 68, 83, 32, 99, 97, 45, 99, 101, 110, 116, 114, 97, 108, 45, 49, 32, 50, 48, 49, 57, 32, 67, 65])] }&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; Client auth requested but no cert/sigscheme available&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; Server cert is [Certificate(b"0\x82\x04\xe30\x82\x03\xcb\xa0\x03\x02\x01\x02\x02\x10\0\xc9\x0b^\x92\x04V\xa9\xd4#b*yh ;&lt;br&gt;
 ...&lt;br&gt;
 \xcc\xf5\xb8\tu\xef\x84\xb9\x84\xd3d\xc0\xf7\xf1\xde\x0b\r\xca\x10r0\x89\xc3n\x11\xfc")]&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; Server DNS name is DNSName("database-1.xq7f5vzbpq1x.ca-central-1.rds.amazonaws.com")&lt;br&gt;
 DEBUG rustls::client::tls12          &amp;gt; Session not saved: server didn't allocate id or ticket&lt;br&gt;
 DEBUG tokio_postgres::prepare        &amp;gt; preparing query s0: SELECT * FROM information_schema.information_schema_catalog_name&lt;br&gt;
 DEBUG tokio_postgres::query          &amp;gt; executing statement s0 with parameters: []&lt;br&gt;
 INFO  tokio_postgres_rustls_rds_demo &amp;gt; postgres&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;There are many great libraries that help you build modern micro-services and native cloud applications in Rust. If you ever find yourself working with PostgreSQL, use Tokio Postgres with Deadpool to build responsive, scalable applications with Rust’s powerful asynchronous constructs.&lt;/p&gt;

&lt;p&gt;To secure your client connection, use Rustls; however, make sure it is configured properly either with &lt;a href="https://crates.io/crates/webpki-roots" rel="noopener noreferrer"&gt;Mozilla’s trusted root certificates&lt;/a&gt; or your service provider’s own root certificates!&lt;/p&gt;

&lt;p&gt;To try the approach outlined in this article, check out the demo project &lt;a href="https://github.com/ecliptical/tokio-postgres-rustls-rds-demo" rel="noopener noreferrer"&gt;hosted on GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Splash photo by &lt;a href="https://unsplash.com/@pawel_czerwinski" rel="noopener noreferrer"&gt;Paweł Czerwiński&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>aws</category>
      <category>postgres</category>
      <category>rds</category>
    </item>
  </channel>
</rss>
