Making use of Container Service Secrets
Container Service Secrets allow you to pass secret or sensitive values into your containers without exposing those values to the users launching the containers.
The primary use case is for providing credentials to containers that need to connect to external services. By launching the container, users can make use of some external API without themselves having the credentials for that API. Instead, the credentials are accessible to the Container Service, which passes them to the container at launch time without exposing them to the user.
This is a new feature. While it enables some use cases, it may not enable all of them. If there is something you want to accomplish with secrets that isn't covered here, please let us know.
What is a Secret
We have added a new "secrets" section to the Command, in which the Command author can specify a list of Secrets that need to be provided to the container.
Each Secret has two parts: a Source that defines where the secret value comes from and a Destination that defines how the value will be provided to the container.
Both the Source and Destination have a "type". Different Source types are the different places that secret values can be stored by an XNAT system administrator and read by the Container Service; different Destination types are the different ways those secret values can be provided to the container.
Both Source and Destination also have an "identifier". What this means will vary by source or destination type, but this will be some kind of name that distinguishes the particular source or destination. For example, with the Environment Variable destination, the Container Service will set an environment variable in the container with a name given by the identifier and a value given by the secret value.
Example
Let's say I have built a container that needs to read some data that I have staged in AWS S3, such as a trained model. I don't want to store this data in the container image itself; I want the container to be able to load the data at runtime. That means I need to pass an AWS access key and secret key into the container.
I arrange with my XNAT system administrator for the AWS access key and secret key to be written into system properties named aws_access_key
and aws_secret_key
. I will use those as the sources. For the destinations, I can write the secret values to the standard AWS environment variables. I add the following to my Command:
{
...
"secrets": [
{
"source": {"type": "system-property", "identifier": "aws_access_key"},
"destination": {"type": "environment-variable", "identifier": "AWS_ACCESS_KEY_ID"}
},
{
"source": {"type": "system-property", "identifier": "aws_secret_key"},
"destination": {"type": "environment-variable", "identifier": "AWS_SECRET_ACCESS_KEY"}
}
],
...
}
When any user on the site launches a container from this Command, the Container Service will read the credentials from the given system properties and set them into the corresponding environment variables. The container gets the credentials and thus gets access to AWS S3, where it can download the data it requires.
Sources
System Property
"source": {"type": "system-property", "identifier": "<system property name>"}
When a Command specifies a Source with type "system-property", the Container Service will attempt to read the value from the Java System Properties. The "identifier" of the Source is used as the name of the System Property.
In order to run this container, an XNAT system administrator must provide the secret value in a system property with that name.
If you run multiple XNAT nodes, any one of them could launch a container so this system property must be defined on each of them.
Example: Let's say an admin wants to install a Command which has a Secret with source type "system-property"
and identifier "my.system.prop.name"
. And the admin knows the secret value that must be provided to this container is "123xyz"
. To enable the Container Service to find that secret value, an XNAT system administrator with access to Tomcat server settings must set a system property by adding -Dmy.system.prop.name=123xyz
to their tomcat CATALINA_OPTS
.
Destinations
Environment Variables
"destination": {"type": "environment-variable", "identifier": "<env name>"}
The secret value will be written to the identified environment variable. Do not prefix the identifier with a $.
The identifier cannot overlap with any environment variable set in the Command's "env" section, nor with any of the reserved values set by the Container Service (XNAT_USER
, XNAT_PASS
, or XNAT_HOME
).
Limitations
Container Service Secrets were built with a particular use case in mind. We do hope it can work for your use cases too. But there are currently some limitations that you should be aware of.
Secrets are defined as part of the Command. Command definitions are "global", i.e. there is one set of Commands for your whole site, which means the Secrets defined within a Command are global too.
- There is no ability to customize the secret values by user or project. Whenever any user launches a container from that Command, the same secret value will be injected into the container.
- There is no ability to customize, say, the name of the system property from which a secret value will be read (beyond editing the Command itself).
As we discussed above, in the first version of Container Service Secrets there is only a single Source type and Destination type: System Properties and Environment Variables respectively. We hope to be able to provide more flexibility in Sources and Destinations in the future.
Secrets can only be defined on "main" Commands, not Setup or Wrapup Commands.
We have ensured that the Container Service will not leak the secret values through any of its APIs. However, we do not control what runs in the container itself; we cannot guarantee that the container will not leak the secret value by, for example, writing it to stdout or an output file. Be careful to only run containers that you trust, especially if they read secret values.