Blog
Flox and AWS: Taking the Chaos Out of Secrets Management
Steve Swoyer | 5 December 2024
So you’re wrapping up this year’s AWS re:Invent, inspired, overwhelmed with new ideas, yet still struggling to process the sheer surfeit of stuff Amazon announced this week. There’s so much you want your team to experiment with, but at the same time you feel blocked by a familiar (and frustrating) challenge.
You still lack a standard, reproducible solution that teams can use to securely manage AWS secrets and authenticate consistently with AWS services, especially in collaborative local development workflows spanning different OSes and machine architectures.
This article walks you through how you can use Flox to create reproducible dev environments for managing AWS secrets, authenticating with AWS services, and automating AWS workflows.
All Flox environments are 100% portable, meaning they’ll run and behave the same way everywhere—in local dev, CI, and production. If you struggle with AWS secrets management in a team-based setting, this walk-through was written with you in mind!
Intrigued? Read on to discover how this works!
Keeping It Like a Secret
For this walk-through, we’ll showcase a Flox environment that combines 1Password, the popular secrets-management service, and the AWSCLIv2, which we’ll assume doesn’t need an introduction.
Secrets management in this environment is handled as follows:
- We authenticate via the 1Password CLI when we first activate our Flox environment;
- We use the 1Password CLI to obtain long-term
AWS_ACCESS_KEY
andAWS_SECRET_ACCESS_KEY
secrets from the 1Password vault in which they live; - We use these to obtain short-term secrets from the AWS Secure Token Service (STS);
- We export our short-term secrets as environment variables to an ephemeral 1Password environment inside the Flox environment. This is enabled via 1Password’s
op run
sub-command.
Got it? Ready to dive in?
Floxifying Secrets Management
If you don’t already have Flox installed, go ahead and download it.
Then pull a copy of my pre-built AWS-1Password example environment from FloxHub and activate it locally:
Or, if you’d rather test drive it first, you can activate it as a remote (i.e., ephemeral) Flox environment:
See those last two lines? The 1Password CLI expects to work with default environment variables and/or read required data from its a pre-populated configuration file. You’ll need to configure these defaults in order for the 1Password CLI to work. (See Housekeeping Notes, below, for a detailed walk-through.)
For the present, let’s assume I’ve already performed the required bootstrapping and due diligence and that I’m champing at the bit to do something useful in AWS. So I’ll start by creating an IAM role I can use to run AWS Lambda functions. To do this, I’ll use the AWSCLIv2’s aws
command:
This successfully generates the following JSON:
In my pre-built Flox 1Password-AWSCLIv2 environment, aws
is actually an alias.
You can create, modify, or destroy any Flox environment by making changes to manifest.toml, the configuration artifact stored in the .flox/env/
path nested inside the environment’s project folder. A Flox manifest consists of several sections, including [profile]
, which is where you define aliases and other shell-specific logic. For my aws-1pass
environment, I defined the following logic in profile
:
This alias wraps the aws
command, using op run to fetch and inject AWS credentials from 1Password. Because of how op run
executes, these secrets are available only for the duration of the command. From my perspective, I’m never prompted to enter my AWS secrets: anything I want to do using aws
just works. The same would be true for my teammates (if I had any) because we’d all be using the same pre-built Flox environment. We’ll come back to this in How this works, below.
For now, let me just note that the aws iam create-role
command behaved exactly as it’s supposed to.
Doing just about anything in AWS
Now I can go ahead and attach some IAM policies to my lambda-execution-role
:
With Lambda and other services, I can use my Flox environment's aws
alias to do almost anything I can do with the real aws
command. If necessary, I can also run the non-aliased aws
executable at any time by typing command aws <sub_command>
in the CLI.
Say I want to create an AWS Lambda function that watches an S3 bucket and triggers when a file with a .pdf
extension gets dropped into it. First I’d build and zip up my Lambda deployment package, just like normal. Then I’d pass this command via my Floxified AWSCLIv2:
Once again, it works! Or, at least, it’s created. Getting my function to run in Lambda will probably require some work. Still, output like this is a good start:
The JSON-looking text is metadata associated with my Lambda function, which should run, assuming that
- My IAM roles and permissions are correctly configured;
- The code in my
lambda_function.zip
deployment package runs correctly; and - This package also includes all required runtime dependencies.
The above example involves Lambda, but you can use the aliased aws
command to do basically anything you might want or need to do in AWS. Like create S3 buckets:
Or upload files to S3:
I kid. I didn’t really upload a 60TB file. (On a side note, does [clearing throat] anyone know if AWS charges for data ingress, as well?) This was just my puckish attempt at showing that anything I can do with the aws
command I can do with my alias, too. Well almost anything.
How this works
As I mentioned earlier, the alias in the [profile]
section of my Flox aws-1pass1
environment basically functions as a wrapper for aws
, the command you use to perform most tasks in AWSCLIv2.
In my testing, the only aws
sub-command that I can't pass to it is aws configure
, which is a special case. It’s the command the AWSCLIv2 uses to bootstrap its own setup, writing data to a config
file that lives in ~/.aws/
. Interestingly, aws configure
expects to write sensitive data, like your AWS Secret Access Key, to this file. In plain text. Don’t believe me? Check this out:
I’ve used the digits of pi to obfuscate my AWS credentials because, again, they’re stored in plain text.
By contrast, my Flox environment doesn’t persist sensitive data anywhere. It does export my AWS credentials as environment variables, but it does this in a neat way. First, it takes advantage of the built-in isolation you get with Flox itself: basically, you can access anything on your system when you’re within a Flox environment’s subshell; however, the tools, libraries, and variables inside this environment cannot be accessed from outside of it, even by other Flox environments! Any variables you export while working in a Flox environment are destroyed the moment you exit that environment.
That’s one layer of isolation. But 1Password gives you another, super-neat way to isolate sensitive data. You can use the op run
sub-command to spin up tasks in an ephemeral 1Password environment, which is kind of like a virtual environment—or a subshell within a subshell. When you type aws
with one or more sub-commands, this transparently invokes op run
, which executes the tasks associated with these commands in an ephemeral 1Password environment. It’s kind of like two layers of isolation.
tl;dr: When you use your environment’s aws
alias to do something, your AWS secrets exist as env variables only for as long as it takes op run
to finish. Moreover, they execute in a subshell inside a subshell.
Housekeeping notes
In our rush to start doing stuff in Lambda and S3, we skipped over what you need to do to bootstrap your 1Password/AWSCLIv2 environment. This is kind of important! So let’s explore this in detail now.
By default, the first time you run op signin
from the 1Password CLI, you’ll see the following prompt:
This is 1Password’s out-of-the-box setup wizard. You will need to enter the following:
- Your team or organization URL
- Your email address
- Your 1Password secret key
This data is used to generate 1Password’s ~/.config/op/config
file. If you’re a user of the 1Password CLI, there’s a good chance this file already exists. Its contents should look something like this:
If this file doesn’t already exist, you could just copy and customize this JSON, using it to bootstrap ~/.config/op/config
yourself. However, with the power of Flox, we can do even better than this!
Using Flox to Bootstrap AWS CLI + 1Password Setup
How much better? As a proof-of-concept, I built an automated setup wizard into my own aws-1pass
environment.
This wizard creates and pre-populates a 1Password config
file with the required data and offers to set up session persistence. It also gives you the option of obtaining a 1Password session token, so you don’t have to re-authenticate each and every time you activate the environment. Since the token expires after a set period of time, it’s especially useful if you need to activate multiple instances of the same environment, or if your workflow involves exiting (i.e., destroying) and re-entering (i.e., re-launching) the environment.
You can try it out yourself by running flox activate -r barstoolbluz/aws-1pass
.
Achieving this involved writing a few hundred lines of shell logic in bash. I could either have included this logic in the [hook]
section of my Flox environment’s manifest.toml
file or put it into a shell script.
I opted for the former pattern, mainly because I want to be able to remotely activate my aws-1pass
environment (using flox activate -r
) from FloxHub so I always get the latest version of that environment. Flox environments are completely declarative, so I can edit and version them directly on FloxHub. By managing my Flox environments centrally and activating them remotely, I get exactly the same experience…everywhere. This pattern scales especially well for teams too. When users flox activate -r
, they run a locked instance of the environment, so they can’t make changes that might break it.
However, if I were to push my environment to GitHub, GitLab, or AWS CodeCommit, I could instead move this logic into a shell script—say, bootstrap.sh
—that lives in the same repo/project folder.
In this pattern, I’d build conditional logic into my Flox environment that checks for the existence of a config
file in ~/.config/op/
and—if this file is missing or unpopulated—runs bootstrap.sh
. This pattern gives me a way to version and manage my project’s code and runtime dependencies in GitHub, GitLab, or AWS CodeCommit. The downside is that it doesn’t provide the same level of control as when users flox activate -r
from FloxHub, because they can make changes to it locally that potentially break it.
Making it your own
So what does this look like in practice? Basically, my shell logic automates three tasks.
1. Bootstrapping the population of the 1Password config
file. We covered this above, in Housekeeping Notes. My Flox environment is specific to 1Password, but it’s simple enough to create environments with built-in logic that bootstraps Hashicorp Vault, AWS Secrets Manager, and other secrets-management tools or services, including Google Secret Manager or Azure Vault.
2. Configuring the specific field names used in the 1Password vault. In 1Password, each item can have multiple fields, and these fields can have custom names. To retrieve your AWS secrets programmatically, you need to know the names of these fields.
My setup wizard prompts you for these field names, persisting them in a session file that's used to store information about your Flox 1Password environment.
This file lives in ~/.config/flox
and is called 1password.session
. Here’s an excerpt:
The variables OP_AWS_USERNAME_FIELD
and OP_AWS_CREDENTIALS_FIELD
should map to the corresponding field names in your 1Password vault.
(Note: There is a complete example of the contents of this file at the end of this section.)
3. Configuring persistence for 1Password sessions. This is helpful if you want to exit your environment and re-enter it without logging back in. This is time-limited: the logic built into the [hook]
section of my Flox environment’s manifest.toml
file (which lives in your project folder’s ./.flox/env/
path) grabs a short-term session token from 1Password. This token usually lives for no longer than 30 minutes.
You can use booleans to turn this on or off in your ~/.config/flox/1password.session
file. See below.
If you enable persistence, your tokens are stored in plain-text in 1password.session
. This is a short-term token, so it shouldn’t be a problem. But your org might have policies against this.
Now, as promised, here are the contents of a populated 1password.session
file in their entirety:
Again, my aws-1pass
environment is a proof-of-concept: an ambitious example of what you can do with a shared Flox environment. I use it across several different Linux and Windows/WSL2 systems, and I'm productive with it. However, it is also customized for my workflow. Think of it as a framework you can build upon and customize to suit your own needs.
Pragmatic secrets management—for AWS and beyond
Secrets management is supposed to make your life easier, but sometimes the opposite is true!
Flox is your secret weapon.
It’s an environment and package manager with superpowers. You can use it to create reproducible build environments of all kinds, including secure, isolated secrets-management environments that run and behave exactly the same way across dissimilar systems—in collaborative development, when you push them to CI, or in the cloud. You can easily share Flox environments or push them to FloxHub. Pulling Flox environments is exactly as straightforward as cloning repos from GitHub.
The best part is that Flox gives you isolation on your own terms. You get transparent access to everything on your local system because you’re working in an isolated environment on your local system. If you’re missing or need to update tools or libraries, there’s no time-intensive VM or container rebuilding.
Just flox install
what you need and push your changes to FloxHub.
Flox is reproducible software builds made simple!
Flox has just a few commands and is easy to learn. This walk-through is a good starting point, and so is our “Flox in Five Minutes” guide. It's free to download and use too. So why not give it a try?