Podman secrets: a better way to pass environment variables to containers

Podman became the standard container runtime with Oracle Linux 8 and later, and I have become a fan almost instantly. I especially like the fact that it is possible to run containers with far fewer privileges than previously required. Most Podman containers can be started by regular users, a fact I greatly appreciate in my development environments.

A common technique of passing information to containers is to use the –env command line argument when invoking podman run. Since passing sensitive information on the command line is never a good idea I searched for alternatives for a local development environment. Podman secrets are a very promising approach although not without their own issues. Before you consider using them please ensure your security department signs their use off, as always. They are pretty easy to translate back into plain text if you have access to the container host!

Podman Secrets

Podman secrets provide an alternative way for handling environment variables in containers. According to the documentation,

A secret is a blob of sensitive data which a container needs at runtime but should not be stored in the image or in source control, such as usernames and passwords, TLS certificates and keys, SSH keys or other important generic strings or binary content (up to 500 kb in size).

Why is this a big deal for me? You frequently find instructions in blog posts and other sources how to pass information to containers, such as user names and passwords. For example:

$ podman run --rm -it --name some-container \
-e USERNAME=someUsername \
-e PASSWORD=thisShouldNotBeEnteredHere \
docker.io/…/

Whilst specifying usernames and passwords on the command line is convenient for testing, passing credentials this way is not exactly secure. Quite the contrary. Plus there is a risk these things make it into a source control system and thus become publicly available.

An alternative to passing information to containers is by using Podman secrets. This way deployment and configuration can be kept separately.

Podman Secrets in Action

Secrets are separate entities, you create them using podman secret create (documentation link). They work quite well, provided they do not contain newlines. A little application using Oracle’s node driver to connect to an XE database and printing some connection details serves as an example. The example assumes that an XE 21c container database has been started. An “application account” has been created, and the password to this account is stored locally as a secret named oracle-secret.

The “application” is rather simple, it is based on the getting started section of the node-oracledb 5.5 driver documentation.

$ cat app.mjs 
import oracledb from 'oracledb';

(async() => {

  let connection;

  try {
    connection = await oracledb.getConnection( {
      user          : process.env.USERNAME,
      password      : process.env.PASSWORD,
      connectString : process.env.CONNECTSTRING
    });

    const result = await connection.execute(`
      select
        sys_context('userenv', 'instance_name') instance, 
        sys_context('userenv', 'con_name') pdb_name, 
        user
       from dual`,
      [],
      {
        outFormat: oracledb.OUT_FORMAT_OBJECT
      }
    );
    
    for (let r of result.rows) {
      console.log(`you are connected to ${r.INSTANCE}, PDB ${r.PDB_NAME} as ${r.USER}`);
    }

  } catch (err) {
    console.error(err);
  } finally {
    if (connection) {
      try {
        await connection.close();
      } catch (err) {
        console.error(err);
      }
    }
  }
})();

The important bit is right at the top: the Connection object is created using information provided via environment variables (USERNAME, PASSWORD, CONNECTSTRING). I locked the oracledb driver version in package.json at version 5.5.0, the most current release at the time of writing.

Running the Container

After building the container image, the container can be started as follows:

$ podmanrun --rm -it \
--net oracle-net \
--name some-node-app \
-e USERNAME=martin \
-e CONNECTSTRING="oraclexe:/xepdb1" \
--secret oracle-secret,type=env,target=PASSWORD \
localhost/nodeapp:0.1

Translated into plain English this commands starts an interactive, temporary container instance with an attached terminal for testing. The container is instructed to connect to the oracle-net network (a Podman network). A couple of environment variables are passed to the container: USERNAME and CONNECTSTRING. The use of the secret requires a little more explanation. The (existing) secret oracle-secret is passed as an environment variable (type=env). If it weren’t for the target=PASSWORD directive the secret would be accessible in the container by its name oracle-secret. Since I need an environment variable named PASSWORD (cf app.mjs above) I changed the target name to match. Once the container is up an environment variable named PASSWORD is available.

The new container should connect to the database and print something along the lines of:

you are connected to XE, PDB XEPDB1 as MARTIN

Summary

Podman secrets allow developers to provide information that shouldn’t be part of a container image or (configuration) code to containers. Provided the secrets are set up correctly they provide a much better way of passing sensitive information than hard-coded values on the command line. I said they provide a better way, but whilst secrets are definitely a step in the right direction they aren’t perfect (read: secure).

Whenever touching the point of sensitive information the advice remains the same: please have someone from the security team review the solution and sign it off before going ahead and using it. There are certainly more secure ways available to provide credentials to applications. Podman secrets however are a good first step in the right direction in my opinion.

Advertisement