Storing Secrets

View the index of available developer guides

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.