Unlocking Acorn’s Native Secret Management

by | Aug 31, 2022

Spread the word

Running apps in production always comes with the need for a database password, api key, or private key. It has always been a challenge to securely deliver sensitive information to the application.

Acorn provides a flexible way to accommodate getting secrets to your application. First, Acorn allows you to define all of the secrets your app might need including configuration files, certificates, passwords, etc. Then Acorn will either use the values provided or auto-generate secrets for the application. The benefit of auto generation is that the secrets can be shared amongst the containers in the Acornfile or linked in from other Acorn apps without exposing the values. This provides a nice, closed loop system, and no one outside of the loop needs to know what these values are. This makes development and testing cycles much faster, but for production you might need some of Acorn’s other capabilities.

Let’s look at an example of a simple web server talking to a DB server.

containers: {
    web: {
        image: "nginx"
        env: {
            DB_USER: "appuser"
            DB_PASS: "password123"
        }
    }
    db: {
        image: "mysql"
        env: {
            MYSQL_USER: "appuser"
            MYSQL_PASSWORD: "password123"
            MYSQL_ROOT_PASSWORD: "root"
        }
    }
}

Looking at the simplified example there are a few things wrong with this picture.

  1. The passwords are in clear text in the app definition, don’t need to say much else here.
  2. The passwords are hard coded with no way to override them.
  3. These passwords are way too simple and easy to guess.

All in all, the above example is just not good. Let’s see how we can improve this. First, we need to define the passwords as secrets and allow it to be shared between the two containers. Defining a secret allows you to use them as variables in the rest of the Acornfile.

containers: {
    web: {
        image: "nginx"
        env: {
            DB_USER: "secret://db-user-creds/username"
            DB_PASS: "secret://db-user-creds/password"
        }
    }
    db: {
        image: "mysql"
        env: {
            MYSQL_USER: "secret://db-user-creds/username"
            MYSQL_PASSWORD: "secret://db-user-creds/password"
            MYSQL_ROOT_PASSWORD: "secret://db-root-password/token"
        }
    }
}
secrets: {
    "db-user-creds": {
        type: "basic"
        data: {
            username: "appuser"
            password: "password123"
        }
    }
    "db-root-password": {
        type: "token"
        data: token: "root"
    }
}

This is better, but we still have hard coded values in the file. This might be OK in some very limited scenarios, but we can do better. Let’s make Acorn generate the passwords for us, we don’t really need to know them if the app and database can communicate. If needed, we can find out the generated credentials or set them to known values at runtime.

containers: {
    web: {
        image: "nginx"
        env: {
            DB_USER: "secret://db-user-creds/username"
            DB_PASS: "secret://db-user-creds/password"
        }
    }
    db: {
        image: "mysql"
        env: {
            MYSQL_USER: "secret://db-user-creds/username"
            MYSQL_PASSWORD: "secret://db-user-creds/password"
            MYSQL_ROOT_PASSWORD: "secret://db-root-password/token"
        }
    }
}
secrets: {
    "db-user-creds": {
        type: "basic"
    }
    "db-root-password": {
        type: "token"
    }
}

There, now we no longer have hardcoded passwords in our file. The generated passwords will be randomly generated and sufficiently complex.

Pass existing secrets

You might be wondering now, how do I pass in known passwords for production services? Glad you asked, a simple approach might be adding args: secretPassword: "", but this could leak sensitive information in shell histories, system logs, etc. as the app goes through the deployment process. The best way is to add the secrets in the cluster ahead of time. Then when you run the application simply swap them in.

In this post we will manually create the secrets, but you can use existing tooling like Vault or SOPs and workflows to inject secrets into the cluster. As long as the secrets are present in the cluster the run command is the same.

To manually create the secrets run the following:

acorn secret create --data @username=user-secret.txt --data @password=password-secret.txt existing-db-creds

acorn secret create --data @token=root-pass-secret.txt existing-db-root-password

Now when we go to launch the application we can swap the secrets in like so:

acorn run -s existing-db-creds:db-user-creds -s existing-db-root-password:db-root-password <APP IMAGE>

Linking in secrets from another Acorn app

App owners and operators have loudly stated, “I wouldn’t use a single DB container for my production stateful app”. In production scenarios we wouldn’t either, our view is that stateful services would be their own deployment or consumed from cloud providers and SaaS offerings. We can address the multiple deployments today, and first-class cloud provider/SaaS provider support is on the roadmap.

Let’s look at a scenario where a team wants to consume the operations team’s Mariadb Acorn app. The Mariadb Acorn app deployed by the ops team exposes the DB credentials through the secret db-user-credentials which contains the keys username and password.

acorn apps
#mysql
acorn secrets
#ALIAS                              NAME                            TYPE        KEYS                  CREATED
#mysql.backup-user-credentials      backup-user-credentials-fnvr4   basic       [password username]   49s ago
#mysql.create-backup-user           create-backup-user-gxgmw        template    [template]            49s ago
#mysql.db-user-credentials          db-user-credentials-9bchj       basic       [password username]   49s ago
#mysql.mariadb-0-client-config      mariadb-0-client-config-g5mpx   template    [template]            49s ago
#mysql.mariadb-0-galera-config      mariadb-0-galera-config-wmmpx   template    [template]            49s ago
#mysql.mariadb-0-mysqld-config      mariadb-0-mysqld-config-rkwhz   template    [template]            49s ago
#mysql.root-credentials             root-credentials-xgstt          basic       [password username]   49s ago
#mysql.user-secret-data             user-secret-data-vh9qj          opaque      []                    49s ago

Let’s build and launch our app leveraging the pre-deployed Mariadb Acorn app.

acorn build -t myapp .

acorn run -s mysql.db-user-credentials:db-user-creds -l mysql:db myapp
#fragrant-sea

If we look at this deployment, we will see that no database container was deployed as part of the app, and a db-user-creds secret was not created. Our application is consuming the db credentials from the mysql.db-user-credentials secret so there is no need for Acorn to generate one. Also notice that Acorn didn’t deploy a db container as part of the app. Behind the scenes Acorn created an alias called db that routes to the operations team database Acorn app.

acorn containers fragrant-sea
#NAME                                    APP            IMAGE                  STATE     RESTARTCOUNT   CREATED     MESSAGE
#fragrant-sea.web-766c5c7886-q69xh       fragrant-sea   nginx                  running   0              7m24s ago
acorn secrets |grep fragrant-sea
#fragrant-sea.db-root-password      db-root-password-lhcxv          token       [token]               8m38s ago

Conclusion

In this post we have gone over the basics on how to work with secrets in Acorn. The mechanisms provided by Acorn allow for flexibility when starting with Acorn or integrating into existing workflows. Secrets can be autogenerated, or pre-seeded into the cluster and consumed at runtime by the Acorn. This allows existing secret workflows to still be used when rolling out Acorn apps.

This is an area we expect will continue to evolve so that secrets become easier to manage throughout the application lifecycle with Acorn. In the future, I will be writing a post about using Hashicorp Vault with Acorn.


Spread the word

Recent Posts