Storing Secrets

Plugins commonly store user credentials and similar secrets, like API keys, access tokens, or just user names and passwords, to interface with other systems and services. Plugins that store such secrets need to be careful how they store them. With simple String fields that get serialized to disk as plain text, a number of problems can occur:

  • On many systems, large parts of the Jenkins home directory is accessible to other user accounts, allowing to retrieve credentials stored on disk as plain text.

  • Backups of Jenkins home can be compromised, disclosing secrets, even when excluding the secrets/ directory.

  • String fields are round-tripped in configuration forms as plain text even if the value is hidden in a password field, so can be accessed via the HTML page source code. This even applies to users who only have the Extended Read permission.

The easiest solution to all of the above is to store the password as a Secret.

  • The key to decrypt secrets is stored in the secrets/ directory which has the highest protection, and is recommended to be excluded from backups.

  • Secret fields are round-tripped in their encrypted form, so that their plain-text form cannot be retrieved by users later. If a user only has the Extended Read permission, the secret is simply removed from output.

A more advanced option is to integrate with Credentials Plugin. See its documentation for more information.

Storing Secrets on Disk

The easiest way to store secrets is to store them in a field of the type Secret, and access that field in your other code via a getter that returns the same type. Jenkins will transparently handle the encryption and decryption for on-disk storage.

An example for how to use a Secret shared between all instances of a build step is below.

public class MyBuilder extends Builder implements SimpleBuildStep {

    public void perform(Run<?,?> run, FilePath workspace, Launcher launcher, TaskListener listener) {
        String password = ((MyBuilder.DescriptorImpl)getDescriptor()).getPassword().getPlainText(); (1)
        // Perform work with the password here, like sending HTTP requests etc.
        // Secret#toString will also obtain the plain text, but it's deprecated and will log a warning.
    }

    @Extension
    public static class DescriptorImpl extends BuildStepDescriptor<Builder> {
        private Secret password; (2)

        public void setPassword(Secret password) {
            this.password = password;
        }

        public Secret getPassword() { (3)
            return password;
        }

        @Override
        public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
            req.bindJSON(this, json);
            return true;
        }

        // more code
    }

    // more code
}
1 To use the password, obtain the plain text.
2 Secret field will store the password encrypted on disk.
3 Secret getter, if used by the f:password form field (below), will round-trip the password on the UI in encrypted form and hide it from users without Configure permission.

Secrets and Configuration Forms

In the configuration form, display the secret itself (rather than the decrypted secret) hidden in a <f:password> field. This requires that the getter or public field used to populate the form element also is a Secret (see above).

  <f:entry title="${%Password}" field="password">
    <f:password />
  </f:entry>

Secrets spanning multiple lines (such as certificates or SSH keys) should be masked from view by using the <f:secretTextarea> form element. It is available from Jenkins 2.171. Plugins with older core baselines can add a dependency on the standalone library io.jenkins.temp.jelly:multiline-secrets-ui:1.0 (or newer) with the same form element. See the documentation in its GitHub repository for usage instructions and sample code.

Encryption of Secrets and Credentials

Jenkins uses AES to encrypt and protect secrets, credentials, and their respective encryption keys. These encryption keys are stored in $JENKINS_HOME/secrets/ along with the master key used to protect said keys. This directory should be configured so that only the operating system user the Jenkins controller is running as has read and write access to this directory (i.e., a chmod value of 0700 or using appropriate file attributes). The master key (sometimes referred to as a "key encryption key" in cryptojargon) is stored unencrypted on the Jenkins controller filesystem in $JENKINS_HOME/secrets/master.key which does not protect against attackers with direct access to that file. Most users and developers will use these encryption keys indirectly via either the Secret API for encrypting generic secret data or through the credentials API. For the cryptocurious, Jenkins uses AES in cipher block chaining (CBC) mode with PKCS#5 padding and random IVs to encrypt instances of CryptoConfidentialKey which are stored in $JENKINS_HOME/secrets/ with a filename corresponding to their CryptoConfidentialKey id. Common key ids include:

  • hudson.util.Secret: used for generic secrets;

  • com.cloudbees.plugins.credentials.SecretBytes.KEY: used for some credentials types;

  • jenkins.model.Jenkins.crumbSalt: used by the CSRF protection mechanism; and

  • org.jenkinsci.main.modules.instance_identity.InstanceIdentity.KEY: used to identify the Jenkins instance.