The continuous integration and continuous delivery (CI/CD) pipeline is a fundamental component of the software delivery process for DevOps teams. The pipeline leverages automation and continuous monitoring to enable seamless delivery of software. With continuous automation, it’s important to ensure security for every step of the CI/CD pipeline. Sensitive information like access credentials is often necessary at various stages of the pipeline. Securing this information is vital to maintaining a strong security posture. This post discusses how to secure secrets within Jenkins CI/CD pipelines.
Using Secrets in CI/CD Pipelines
As previously mentioned, CI/CD pipelines often need access to sensitive information like passwords and API keys. Hardcoding them in plaintext represents a critical security risk and violates auditing guidelines in most compliance frameworks. A mechanism to make use of these credentials without exposing them unnecessarily is important for automating software delivery.
The collection of tools and techniques used to store, manage, and access these credentials securely is known as secrets management. Secrets are broadly used in IT, DevOps, and cloud environments.
Secrets include:
● App-generated passwords
● System-to-system passwords
● API keys
● Private encryption keys
● One-time passwords
● Certificates
● Privileged account credentials
CI/CD pipelines integrate multiple configuration management platforms, requiring secrets to enable service-to-service communication. These platforms also need secrets to authenticate entities requesting access to SSH servers, databases, HTTP services and other resources.
The Purpose of Secrets in CI/CD
Secrets are used to manage access permissions for entities within the CI/CD pipeline. Some use cases include source control management, infrastructure connection, collaboration providers and verification. Let’s discuss each of these use cases in more detail.
1. Source Control Management
Version control systems, such as Git, foster collaboration between multi-personnel teams. These systems help maintain cohesive application development by managing and storing code contributions from multiple developers. Since source code can comprise some of an organization’s most valuable assets, it is important to properly authenticate and manage access permissions for Git repositories.
Secrets management systems are important in securing credentials used to connect contributors’ machines to the repositories. The secrets themselves are not stored within version control, as malicious users can access them and orchestrate security attacks. Instead, they are stored in special, sensitive files that are designed to be excluded from the repository’s storage and logs.
2. Infrastructure Connection
CI/CD pipelines are often composed of different services residing on multiple hosts distributed across a disparate infrastructure. These hosts constantly communicate with each other, as well as with services through API calls, to achieve the required application functionality. Secrets such as API keys and system-to-system passwords enable a secure connection between the infrastructure running these services, allowing for multi-tenant and multi-cloud CI/CD pipelines.
3. Collaboration Providers
CI/CD pipelines encompass collaborative activity across multiple development, operations and quality assurance environments. These teams rely on multiple tools and frameworks to collaborate and share information. Using secrets for collaboration helps teams ensure secure communications and provides a layer of trust between both parties.
4. Verification
Identity verification is a crucial element of CI/CD and application security. Secrets are used to ensure security and privacy for CI/CD pipelines by connecting applications to identity and access management (IAM) solutions. These solutions provide an external database of user roles and permissions, letting users log in to the applications without exposing their credentials within the CI/CD pipeline.
Examples of Secrets Usage in CI/CD
To set the stage for improving secrets management, it’s helpful to understand some simple use cases. In this section, we’ll provide some basic examples of CI/CD configuration that requires access to sensitive credentials or other data for functionality.
1. AWS Credentials
As one of the leading cloud SaaS platforms, AWS is a common choice for most cloud-based infrastructures. Interacting with the AWS API to provision and query resources typically requires the use of a secret key/access key credential pair. Protecting these credentials is critical to maintaining the security of an AWS account.
2. Infrastructure-as-Code (IaC)
Modern application architecture often depends on infrastructure as code (IaC) to deploy the underlying systems that the workloads run on. Codifying this configuration makes it much easier to maintain a repeatable, scalable and secure infrastructure. In the course of provisioning this infrastructure, engineers will often need to deploy sensitive information to the nodes, such as SSH keys and API credentials. Automating infrastructure provisioning via IaC and CI/CD is a common pattern and managing secrets is an important component of that workflow.
3. Environment Variables
Environment variables, a common convention for configuring processes on a computer, see consistent usage in most CI/CD pipelines. The ability to dynamically set values and retain them for consumption by other processes and scripts is a powerful abstraction when dealing with CI/CD workflows. In many cases, sensitive values will need to be passed via environment variables, again highlighting the need for effective secrets management.
Securing Secrets Within Jenkins
Jenkins requires access to a large number of credentials to interface with all the platforms and applications that make up a complete CI/CD pipeline. Jenkins ships with a default credentials plugin that offers a built-in secrets store. It also comes with the credential binding plugin which enables the binding of credentials to environment variables.
Storing secrets in the Jenkins controller enables applications in the pipeline to reference secrets multiple times, even when they’ve only been created once. While the Jenkins credentials plugin offers a simple way to store and manage secrets, any Jenkins administrator and pipeline author can access them. Therefore, DevOps teams should ensure proper configuration of access rights to keep the CI/CD pipeline secure.
This section discusses some of the available options for securing secrets in Jenkins.
Types of Secrets Jenkins Accepts
Credentials supported by the Jenkins secrets plugins include: Secret text, username/password pairs, secrets file, SSH username and certificates. For a limited number of secrets, these can be stored individually, whereas a large number is typically best managed with a credentials file.
Managing Secrets in Jenkins
If you’re ready to inject secrets safely into your Jenkins pipelines, follow these best practices.
1. One-Time Passwords (OTPs)
One-time passwords help prevent user account takeovers by ensuring that a username-password combination can not be used more than once. While the account retains the username throughout, the user is sent an OTP every time they log in. This keeps Jenkins administrators’ and users’ accounts safe, preventing secrets from being exposed to unwanted individuals.
2. Avoid Hard-Coding Secrets
Remove any secrets that have been hardcoded into CI/CD configuration and Jenkinsfiles. This keeps sensitive credential information away from the pipeline, making it inaccessible to malicious actors.
3. Reduce Attack Surface by Storing Secrets in Multiple Jenkins Files
Storing secrets in multiple files helps enforce the principle of least exposure for credentials. This makes credentials available to the minimum number of applications and users possible.
4. Use Authorization Controls to Manage Secrets Permissions
Developer teams should configure security checks to ensure the application requesting resource access has the properties of a valid application. These applications should be able to access various resources based on permissions assigned in RBAC policies.
5. Follow the Principle of Least Privilege
Only grant permissions to applications that absolutely need access to secrets within the pipeline. Teams should consistently audit permissions to maintain the principle of least privilege throughout the pipeline’s lifecycle.
Managing and Storing Secrets
This section walks through three basic examples of actually using Jenkins with secrets and the Akeyless secrets management platform.
● The first example will highlight a completely insecure example: Using plaintext environment variables to store sensitive credentials.
● The second example will focus on using native Jenkins features and plugins for secrets management.
● The third example will show some of the benefits of using a third-party tool for secrets management.
Plaintext example
To get started, we’ll create a basic “freestyle” project in Jenkins, and connect it to a GitHub repository:
Figure 1: Project creation
Regardless of how we choose to manage our secrets in code, it makes sense to use the Jenkins built-in mechanism for credentials to access the GitHub repository:
Figure 2: Configuring GitHub credentials
Our actual code will be a very basic Python application that makes an API call. The API requires a simple API key in the form of a text string for authentication:
#!/usr/bin/env python
import os
import requests
api_key = os.environ[‘API_KEY’]response = requests.get(“https://thedogapi.com/v1/breeds?api_key={}”.format(api_key))
print(response)
Jenkins provides the ability to configure environment variables; however, without additional plugins, they can only be set globally to the Jenkins environment or instance:
Figure 3: Configuring a global environment variable
The code will read the API key from the environment variable that was just configured. Here’s the configuration for the build steps:
Figure 4: Build configuration
The build steps run some simple linting and then actually execute the code. Here’s a look at part of the log from a successful build:
Figure 5: Build log with exposed secret
The API key has obviously been exposed in a plaintext log with a simple directive in one of the build steps. A minor misconfiguration could potentially cause a breach or compromise.
Jenkins Secret Text Example
Now we can try to improve the security of our build environment by using the built-in credentials handler:
Figure 6: Configure build environment to use secrets
In this example, we’ll use a simple “Secret text” credential binding to store the API key:
Figure 7: Adding a secret credential
Next, we configure our build job to use the new credential variable:
Figure 8: Updated build configuration
Now, after running another build job, we check the build logs again:
Figure 9: Build job with obfuscated secret
Even though we configured the build step to print the variable contents to the log, the Jenkins credential handler knows that the value is sensitive and obfuscates the actual value in any log or output.
Although this measurably improved the security of the build, there are some caveats. While this works well enough for a small Jenkins deployment with a limited number of users, managing access and correct permissions scoping for multiple users and teams across a large, distributed Jenkins architecture quickly becomes complicated and difficult to manage effectively. In addition, the secret value stored in the credential variable is still accessible to every Jenkins instance in the cluster, as well as any user with the appropriate permissions.
If any node is compromised, secrets across multiple deployments and contexts could be at risk. Users still have to manually secure and configure Jenkins infrastructure that now doubles as secrets storage. What if there were a way to use sensitive credentials without ever having to store them in Jenkins itself?
Secrets Management Tool Example
Let’s try using Akeyless to manage our secrets. Since we’re integrating with a Jenkins deployment, we can follow the documentation for working with Jenkins. Akeyless integrates with the publicly available Hashicorp Vault plugin, so it’s simple to get Jenkins set up.
Next, we’ll create a free account with Akeyless, and configure a secret:
Figure 10: Creating a secret in Akeyless
Figure 11: Creating a secret (cont).
The next step is to create an Access Role that has permission to access the secret that was just created:
Figure 12: Create a role
Now we need to give our Access Role the correct permissions to view the secret:
Figure 13: Giving the role permissions
The last thing we’ll need to create on the Akeyless side is an authentication method. This is how we can give external entities, like our Jenkins deployment, the ability to make API calls to Akeyless and retrieve secrets:
Figure 14: Create an auth method
Once the authentication method has been created, be sure to save the Access ID and key by downloading the CSV file; the values will not be displayed again otherwise. Finally, we need to associate our newly created role with this access method:
Figure 15: Associating the auth method with the access role
The next step is to configure the Jenkins build environment according to the Akeyless documentation referenced in a previous step:
Figure 16: Jenkins build configuration with Akeyless
Now we’ll run another build, this time configured to utilize the Akeyless vault we’ve configured:
Figure 17: Build log with Akeyless configuration
In the logs, there is now a step present where Jenkins retrieves the secret from the Akeyless Vault. The credential value is also still obfuscated in the build log. We’ve now got a working, third-party secrets management platform integrated with our Jenkins instance.
It may seem like extra steps just to achieve the same outcome, but an external tool focused entirely on secrets management is a powerful abstraction. Jenkins is a CI/CD tool first and foremost, with secrets management provided as an add-on. In that context, it will never be able to provide the first-class features, management capabilities, and security guarantees that a dedicated secrets management platform can:
● Managed infrastructure with SLA guarantees
● Zero-knowledge encryption
● Intuitive, accessible secrets management and organization
● Integration with industry-standard DevOps tools and platforms
A tool like Akeyless also does away with having to manage complex, self-hosted infrastructure. We were able to have a working secrets endpoint in just a few clicks.
Conclusion
Since security is a top consideration for CI/CD pipelines, it is important to follow best practices when using secrets in Jenkins. Jenkins pipelines rely on secrets for identity verification, collaboration, and infrastructure management. CI/CD secrets management presents a challenge for most teams because of how complex it is to implement access control at a granular level.
With a secrets management tool, Jenkins users get a centralized and secure resource to manage the many credentials that are required to operate CI/CD pipelines. They benefit from using a simplified, standardized approach to access all secrets they need, without worrying about security. Simply put, a secrets manager accelerates DevOps workflows.
Meanwhile, security teams benefit by gaining full visibility into where secrets are, and how they are used. With automated processes and centralized access policies, a secrets management platform helps security teams stay compliant with security regulations as well.