-
Notifications
You must be signed in to change notification settings - Fork 45
Secure secrets in repositories
Secrets in a repository need to be hidden from unauthorized parties. This document outlines how Jervis accomplishes that. See also Travis CI documentation on the subject. The security in this project is loosely based on my research in respository-secrets.
Jervis provides secure field support through the securityIO
class (see Jervis groovydoc). By default Jervis uses asymmetric encryption to secure the fields; specifically RSA public-key encryption. The option to make the encryption stronger is available. All decrypted strings are then used as environment variables in the build. This functionality is provided by the Mask Passwords plugin.
Before bothering to secure secrets in a repository, Jenkins should be secured. In addition to following Jenkins best practices, I recommend the following additional precautions.
- The Job DSL runtime runs on the master. Therefore, only grant the ability to create jobs to very trusted parties because anybody writing Job DSL scripts is basically an admin through scripting. Because of this, the private key only exists on the Jenkins master when decrypting secure properties.
- Secure communication with Jenkins by hosting with TLS (i.e. HTTPS).
- Ensure Jenkins requires a username and password. Otherwise, it will just be a web-based shell and easily compromised.
- Enable CSRF mitigation support in Jenkins.
- The master should have
0
executors. If it has executors then it should be an EXCLUSIVE slave and all jobs on the system must have labels to execute on their appropriate agent. - Build jobs should never execute on the master. If a user gets a hold of the master
${JENKINS_HOME}/secret.key
file then they can effectively compromise all secrets and passwords on the system. - Additionally, jobs executing on the master can disable security by modifying
${JENKINS_HOME}/config.xml
. - Encrypt all communication between the master and slave. Whether you use SSH from master to slave or the new secure JNLP3 support from slave to master. SSH is tried and true while JNLP3 is still new and hasn't really been tested thoroughly.
- Track your
$JENKINS_HOME
configuration with git and don't forget to separately backup$JENKINS_HOME/secret.key
.
There are a few fields in the .jervis.yml
YAML file that support security. Here's example YAML.
jenkins:
secrets_id: "<Jenkins RSA key identifier>"
secrets:
- key: MY_ENVIRONMENT_VAR
secret: <rsa encrypted string>
- key: ANOTHER_ENVIRON_VAR
secret: <rsa encypted string>
The secrets_id
is a Jenkins credentials ID which will be explained further in this document.
Since the users can access passwords via secure properties (environment variables) then they can manage their own secure file support using symmetric encryption. They could simply store the symmetric key pass phrase as an environment variable.
A user can configure Jenkins with their own RSA private key to decrypt secrets. This means they can decide what key length they want and aren't limited by the key size other than what openssl supports. In Jervis, projects are generated in the format of <Jenkins folder>/<project name>
. The Jenkins folder
is where the credentials must be stored. This separates credentials on a per-folder basis.
Generate private and public RSA keys. The public key is used to encrypt secure properties and the private key is used within Jenkins to decrypt.
#generate private key (recommended to use 2048-bit or 4096-bit RSA keys)
openssl genrsa -out ./id_rsa 2048
openssl rsa -in ./id_rsa -pubout -outform pem -out ./id_rsa.pub
Add the RSA private key to Jenkins.
- Go to the project folder in Jenkins.
- Click on the
Credentials
link in the left hand menu. - Add a new domain and call it
secrets
. - Add a new include filter and set it to
localhost
or some other arbitrary value so that it can't be referenced in jobs. - Save the domain and enter the domain.
- Click Add Credentials.
- Add SSH Username with private key. The username doesn't matter so just set it to
secret
. - Enter the private key directly and paste in the RSA private key.
- Click on the advanced button and set the
ID
to a unique string. This is called the Jenkins credentials ID. - For clarity, set the description to be the same as the credentials ID.
Once you've configured your key in Jenkins add the following to your Jervis YAML file.
jenkins:
secrets_id: "<Jenkins credentials ID>"
This can be easily be done by saving
id_rsa.pub
with the project and usinggenerate_secret.sh
Now that you have the private key installed in Jenkins let's encrypt secure properties using the public key.
echo -n 'plaintext' | openssl rsautl -encrypt -inkey ./id_rsa.pub -pubin | openssl enc -base64 -A
In the example above, secretplaintext
is the text one wishes to encrypt. The result will be cipher text.
Add to your Jervis YAML the cipher text and give the key an environment variable name. This secure property will then be accessible in build jobs as an environment variable.
jenkins:
secrets_id: "<Jenkins credentials ID>"
secrets:
- key: "ENV_VAR"
secret: "<cipher text of secretplaintext>"
Now, when you generate a Jenkins job it will be generated with secure properties added via the Masked Passwords plugin.
To see an example of this check out the Jervis Secrets Test repository.
The private key can be encrypted using the AES algorithm with the ssh-keygen
program. Here's an example.
chmod 600 id_rsa
ssh-keygen -p -f id_rsa
When you add the credential to Jenkins just be sure to set the password to the credential.
As of Jervis 1.0 release, RSA private keys smaller than 2048 bits are no longer allowed. If a Job DSL script or pipeline attempts to load a private key smaller than 1024 then the Job DSL script or pipeline script will fail with a KeyPairDecodeException
. While 2048 bit keys are the minimum allowed, 3096 bit RSA private keys are recommended.
Users should rotate their secrets, rather than migrate, because their data was encrypted using a known broken RSA algorithm. Data encrypted with this weaker algorithm is not considered secure and will still be accessible in git history. Even if the git history is "modified" it may still exist in cloned copies. It is safer to assume the encrypted data was compromised.
To migrate to larger RSA keys do the following:
-
Generate a new private key (do not set a password, yet):
ssh-keygen -b 3096 -f id_rsa_3096 openssl rsa -in id_rsa_3096 -pubout -outform pem -out id_rsa_3096.pub
-
Decrypt your old secrets using the weaker 1024 bit RSA key (let's say it decrypts to
plaintext
).echo 'ciphertext' | openssl enc -base64 -A -d | openssl rsautl -decrypt -inkey id_rsa_1024
-
Encrypt your plain text secret using your new larger private key.
echo -n 'plaintext' | openssl rsautl -encrypt -inkey ./id_rsa_3096.pub -pubin | openssl enc -base64 -A
-
Encrypt your private key with AES before uploading it to Jenkins.
ssh-keygen -p -f id_rsa_3096
- Build overview
- Supported languages
- Supported build tools
- Publish results
- Additional topics:
- Quickstart
- Background information
- Knowledge checklist
- Required Jenkins Plugins
- Labels for Jenkins Agents
- Key security concepts
- Operationalize Jenkins
- High availability
- Disaster recovery
- Pipeline support
- Extending support