hashicorp-vault-plugin
hashicorp-vault-plugin copied to clipboard
Support hudson.util.Secret for JCasC SecretSource variables
For new instances it makes sense to have plain text environment variables.
However a long running modern production Jenkins instance might have autoscaling and a long lived config through jenkins config as code (JCasC).
Feature Request
For AppRole auth and other JCASC environment variables, supporting encrypted hudson.util.Secret strings is desirable.
This could be implemented in VaultSecretSource.getVariable() method.
Related to #170.
Better bootstrap of long running Jenkins instances is my line of thinking.
Not sure how feasible this is without support coming from JCasC
https://github.com/jenkinsci/configuration-as-code-plugin/issues/1141
Does not require JCasC support
All of the code and implementation would exist in this repository.
Example implementation
hudson.util.Secret is a core Jenkins API. You could edit the following code.
https://github.com/jenkinsci/hashicorp-vault-plugin/blob/2e2760b322ee470e638437187616f46b0a89fadf/src/main/java/com/datapipe/jenkins/vault/jcasc/secrets/VaultSecretSource.java#L226-L228
Here's an example implementation which will probably work as intended. Generally, I recommend import hudson.util.Secret; but for brevity I skip that in the following example to reference it directly.
private Optional<String> getVariable(String key) {
Optional<String> value = Optional.ofNullable(prop.getProperty(key, System.getenv(key)));
if(value != null) {
value = Optional.ofNullable(hudson.util.Secret.fromString(value.toString()).getPlainText());
}
return value;
}
The intent being you could set an environment variable like the following.
export CASC_VAULT_APPROLE='{ABC1234AAAAAQ1/JHKggxIlBcuVqegoa2AdyVaNvjWIFk430/vI4jEBM=}'
export CASC_VAULT_APPROLE_SECRET='{DEF5678AAAAAQ1/JHKggxIlBcuVqegoa2AdyVaNvjWIFk430/vI4jEBM=}'
This implementation would enable CASC_VAULT_FILE vault properties file to also support encrypted secrets in its file so you're not storing secrets as plain text on disk.
Reasoning
This is especially important in backups. I store the Jenkins secret keys ($JENKINS_HOME/secret*) separately from the Jenkins backup so that Jenkins backup config is secured (encrypted at rest).
In a long-lived production configuration, an admin could run hudson.util.Secret.fromString('plain text').getEncryptedValue() in the script console to get the desired encrypted string. This would be typical for a migration from an existing Jenkins instance to using JCasC.
How I would bootstrap new environments
I would have a $JENKINS_HOME/init.groov.d/init-jcasc.groovy initialization script which would do the following.
- In AWS, there would be an instance role to AWS Secrets manager granting read access as well as read access to an S3 bucket containing
jenkins.yamljcasc file for dev/staging/prod blue/green. - If
$JENKINS_HOME/vault-secretsource.propertiesfile does not exist; create it pulling properties from AWS secrets manager. - If
$JENKINS_HOME/jenkins.yamlfile does not exist; copy it from S3. (use SHA256 hashing to verify it is up to date or update it). - Reload JCasC via Java APIs. I verified through code review that a configuration reload would re-intialize all Secret sources including VaultSecretSource.
What are your thoughts on my proposed solution? I could open a PR but I might need help with mocking/testing as that's an area where I'm weak.
I could compile it and run it on a live Jenkins instance manually to verify it works.