Manageably safe and secure

“padlock lot” by Priscilla Serneo on Unsplash

Managing flexibility in software is key to adapting to change. Since change was a given long before the software industry realized this, it should be a given it’s worth optimizing for. Configuration is one way to manage flexibility in an application. In the last post, I drew out a picture for managing configuration cleanly, easily, and safely with environment variables in Rust applications. For many use cases this is a perfectly reasonable approach. Minding changes in approaches to application architectures and landscapes, security continues to bubble up as a weak spot in many systems. This is a post extends ideas from the last post to another special class of configuration, affectionately referred to as secrets. Hopefully I will convince you that managing configuration flexibly pairs well with managing security 🍷🤫.

Disclaimer: I am not a security expert. To a large degree this post is motivated by the fact that neither are many of the software engineers responsible for production systems!

In software, a secret is piece of information that can be used to comprise the integrity and security of your application. Imagine what would be possible with leaked access to your customer database password or payments api key 🏦 😱. The value in keeping your secrets safe extend well beyond any specific programming language borders, but in this post I’m going focus on Rust. Rust is an interesting language to explore this topic with because it’s also affectionately referred to as a very safe programming language. Safe in the sense that it can make many kinds traditional software defects that can compromise the integrity and security of your software impossible 🤯.

Sharing a secret

What’s so insecure about storing secrets in environment variables? As mentioned in the previous post, environment variables are the simplest approach for injecting configuration into your application. Simple because shell’s make it easy to export data in plain text for a process to interact with. Exported env information is then made available to any other part of that application and potentially any spawned child processes. There are no scoping rules for environment variables within a given process. Considering containers ( i.e. docker ) you can provide environment variables to containers that may actually run multiple processes, sometimes called sidecars. Exposure of this information may be and is often unintentional. A common approach to security is that access should be a privilege, not a right. When storing secrets in environment variables you are enabling potentially unintentional right to access to many components of your software stack, in plain text!

Organizations should also be aware that security risks related to leakage of secrets do not always come from attacks on the outside but more often than not the weaker link comes from within organizational walls.

cat ~/.bashrc# ping phil in slack for other credentials
export DB_PASS=p@$$w0rD

Environment variables have a way of spreading when left unmanaged. You will often find copies of these secrets stored in local configuration files and shell initialization scripts on developer machines, making it easier for them to run applications locally. The risk this puts on the organization is then not only protecting against leakage in production but also leakage by way of access to an employee laptop. It’s not uncommon for employees using unmanaged env configuration systems to share these secrets over communication channels like slack, at which point these values now live on slacks servers. The practice of manually copying these around also suffers from the phenomena of forgetting what you’ve copied to your clipboard and accidentally posting in public channels. Whoops!

Security practices that help mitigate these security risks exist in the form of encryption and secret management stores. Many of these are very useful software packages. Depending on your organizational structure you may have a separate team that operates one of these. You should use it if you do! One thing to consider with these systems is that they require operational ownership, management and expertise to run. For large organizations this box is likely checked. Many smaller software startups use a slightly alternative mindset. Rather than manage these systems themselves in house, they rely on a externally managed service to provide the same value for them on a pay by usage scale. This is the serverless mindset ( yes, serverless is about more than functions ). For many organizations it is often less expensive to buy a toaster resulting in better utilization of engineering resources.

A clandestine store house

I’m going to focus a loupe in on one such managed service AWS provides called Parameter Store. Parameter Store combines the useful utility of configuration as a service as with AWS’s IAM, which provides access control as a service, and KMS which provides encryption as a service. The latter is a means of encrypting and decrypting on demand without the need to store encryption keys locally ( or on your clipboard ). Parameter Store has one small product/marketing quirk in that it’s actually part of a suite of services called AWS Systems Manager. Parameter Store is the gem of that suite of services 💎✨.

What makes Parameter Store more appropriate for secrets configuration than environment variables? Three things: built-in security primitives ( SecureString is a value type ), centralized management ( removes the habit of storing values ), access control ( controls who can access values ).

From configuration envy to secret crush

If you’re reading this you’re probably wondering how I’m going to tie this back Rust? As hinted in the last post, I’ve been exploring envy’s ideas applied to secrets with AWS Parameter Store in a new crate called envy-store. As a refresher, envy is a crate that works in concert with Rust’s serde ecosystem, a which provides API’s for {ser,de}ializing data safely into native Rust types efficiently and flexibly all at compile time. Envy is essentially a env deserializer for serde. Envy store is also that, but deserializes AWS Parameter Store data instead.

There are a few envy store design choices influenced by Parameter Store that are worth calling out. Parameter Store values are associated with names that can encode a path-like hierarchy. In fact, it’s actually an AWS recommended practice to exploit this naming convention for a few good reasons. It’s useful to note all Parameter Store data exists within the scope of AWS account. This AWS account may be home to many applications that run on compute resources owned by this account. Overtime the management of secrets in a shared namespace can become unmanageable. Using a name hierarchy can enable self-describing ownership for parameters. In practice, it’s a common convention in Parameter Store to prefix parameter names with the name of the application they are used by. /grand-rustacean-central/dbpassword for instance may be the name of a Parameter Store value for an app called grand-rustacean-central🚂 🦀. Creative use can also allow for configuration scoped by environments where variation between environments exist, /grand-rustacean-central/dev/dbpassword. When you use a naming hierarchy, you also get an out of the box performance improvement in the way you query for parameters using the Parameter Store API. Parameter Store has a specialized API for doing just that which in practice is is much faster than it’s alternative. Lastly, you can also use this naming hierarchy to control access to data using IAM service policies.

Technically envy store works very much like the envy crate. It uses parameter names to deserialize into members of a type safe struct. It was also a goal to encourage the use of the AWS best practice of name prefix hierarchies. That ended out surface in envy stores Rust API which accepts a AsRef<Path> type representing that prefix. Another notable part of it’s interface relates to the fact that Parameter Store is an external configuration service. A tradeoff for added security and external management is that there is a runtime dependency on a network service. This is typically the same tradeoff for any micro services architectures. As such, envy store’s interface returns a futures::Future type which encodes both the deferred availability of a value as well as the potential for network or authentication failure.

You can easily create and manage parameters using either the AWS web console or cli. Below is an example of provisioning values using the cli followed by an example of how they could be consumed in the application.

Note: AWS Parameter Store has a primitive notion of parameter value types: String ( plain text value ), SecureString (encrypted text), StringList (comma delimited text values).

Using the aws cli to manage parameters
Rust application that consumes parameters

Additionally useful of note is the fact that you can separate the permission for who can create and edit secrets from those who can read them, enabling the security practice of separating roles and responsibility. In the majority of cases your application will only need read access to the values. You can further enhance the security model of this system by provisioning the application’s runtime with an IAM role that can only read values under the applications Parameter Store prefixed path. The AWS IAM system is very powerful and I highly recommend you read deeper up on its documentation. The only permission an envy store application requires to read values is ssm:GetParametersByPath. These permissions apply to the IAM user the request is authenticated as. In the case of envy store, it uses the same credential chain nearly all other API tooling is afforded by way of the fantastic rusoto crate in order to resolve an IAM identity.

In summary, when considering how to make your software flexible to change with configuration consider how that flexibly affects security when secrets are involved. AWS Parameter Store is a very mature system for managing those for you so you can instead focus on your application and less how to run your own secret management system. ;) With Rust, you can extend that security even further into your type system. Envy store transparently and safely deserializes secrets and other parameter data into the types your application expects without costing application complexity.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Doug Tangren

Doug Tangren

540 Followers

Meetuper, rusting at sea, partial to animal shaped clouds and short blocks of code ✍