withAWS() puts secret key into Jenkins log file if env vars are printed...
Description
Secret key is readable in the Jenkins pipeline log file.
Steps to Reproduce
- In your pipeline use withAWS in option block of declarative pipeline:
def AWS_ROLE = "arn:aws:iam::321724181329:role/QEAutomation" // AWS role used to create EC2 infrastructure (instances, DNS entries...)
def AWS_ID = "AwsDspDevAutoAccount" // ID of Jenkins credentials that safely stores AWS keys
def AWS_REGION = "us-east-1" // Region to create EC2 infrastructure in
withAWS(region: AWS_REGION, role: AWS_ROLE, credentials: AWS_ID)
- Print env vars (in this instance on windows):
echo "ENVIRONMENT VARIABLES FOR THIS BUILD:" echo "set"
Expected behavior: AWS secret access keys should be scrubbed from log (****) like username and passwords pulled out of withCredentials()
Actual behavior: [What actually happened] AWS key, secret and session token are visible in log file:
AWS_ACCESS_KEY_ID=ASIAU11111111GH7B
AWS_DEFAULT_REGION=us-east-1
AWS_REGION=us-east-1
AWS_SECRET_ACCESS_KEY=1111++5bT/WEVfD
AWS_SESSION_TOKEN=11111wEaDLzAu+u2D/gC5pAsiCL8ASAxtPdkR+ouQHwAD4B/3SV0miFuWT1ENRgEmj/PBz3AYz6E1RW3OgeSFo9qVgermFiqSab2O7vMWTei3G7mmo+/m2O9uoMgFHpgVVkCceco+PlolpAcRBKtSWVunL7XDfhhIV9zaC+CiZqxuS2VizsVT0JulTcBZaljHMLX1Mrrv7i7EB5e9e/3Psl34o/vmi0rkm1m49bqn3YP0la6WdoOrBBH6tyuN68dCuMsLxwT8j1Q0ANZktQONURauPUTYopw2g7ch2/jRuO46v46GGf8E9fQOjunaLFpTfavKjIjE+fOvU3BH2GT/1111111111111111111111111==
Hi, this sounds like a good idea, but this is not something this plugin cloud fix. Jenkins should detect the Env Vars and remove them from them log. The plugin just sets normal environment variables. But if you know how I can fix this, I am happy to do it.
@hoegertn the masking of sensitive information is done in the withCredentials() step: https://jenkins.io/doc/pipeline/steps/credentials-binding/:
The secret(s) will be masked (****) in case they are printed to the build log. This prevents you from accidentally disclosing passwords and the like via the log.
The Credentials Binding Plugin does this via its SecretBuildWrapper object:
https://github.com/jenkinsci/credentials-binding-plugin/blob/master/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/SecretBuildWrapper.java
If you implement this then there would no way to have the AWS key, secret and session token in the log file if a pipeline prints any of these env vars in the pipeline log.
Honestly, I do not understand how to use this to create secret environment variables.
To be clear we don't need to create 'secret environment variables' but rather have the AWS secrets masked when ever they are printed to the log file. The environment variables are just an example of how AWS secrets could be printed mistakenly to the log file.
But how should the plugin prevent printing secrets to the console?
That's why i linked the https://jenkins.io/doc/pipeline/steps/credentials-binding/ plugin source above as that plugin masks passwords if they are printed to the build log.
I have 2 issues with this plugin:
- unmasked output of secrets
- doesnt export
AWS_SESSION_TOKENwhen the role is set in the credentials
Both are solved by using withCredentials as suggested by @mattemoore
For example, I have a credentials created by doing:
- Navigate to Jenkins > Credentials > System > Global credentials
- Hit Add Credentials in the left menu
- Fill out the form as follows:
- Kind: AWS Credentials
- ID:
my-example-creds - Access Key ID / Secret Access Key: leave blank if using EC2 instance role or fill in normally
- IAM Role Support:
- IAM Role To Use: arn:aws:iam::123456789:role/MyExampleRole
Then trying to use this plugin I have something like:
withAws(credentials: 'my-example-creds') {
sh 'env'
sh 'aws sts get-caller-identity'
}
Which prints out:
AWS_ACCESS_KEY_ID=ASIA6KZ24U
AWS_SECRET_ACCESS_KEY=hsumKz15FNRaOli4Eki1J
And then the get-caller-identity call fails with the following error because AWS_SESSION_TOKEN was not set.
InvalidClientTokenId The security token included in the request is invalid.
Contrast that with running this:
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: 'my-example-creds']]) {
sh 'env'
sh 'aws sts get-caller-identity'
}
Which outputs:
AWS_SECRET_ACCESS_KEY=****
AWS_ACCESS_KEY_ID=****
AWS_SESSION_TOKEN=****
Note that it is masked and AWS_SESSION_TOKEN is defined.
Then also the get-caller-identity call succeeds:
{
"Account": "123456789",
"UserId": "AROA6KS6TCS:MyExampleUser",
"Arn": "arn:aws:sts::123456789:assumed-role/MyExampleRole/MyExampleUser"
}
withAws(credentials: 'my-example-creds')
is far more expressive and readable than
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: 'my-example-creds']])
so it'd be really nice if under the hood they did the same thing.
Maybe there is a way that if you get a call to withAws that only provides a credentials param, that is somehow passed off to withCredentials for handling?
This is the plugin that provides the AWS Credentials option when creating credentials:
https://github.com/jenkinsci/aws-credentials-plugin
This is the plugin that provides the withCredentials step and does the masking:
https://github.com/jenkinsci/credentials-binding-plugin
I got stumped on this too, is there a plan to fix this? thanks
For declarative pipeline, initialize AWS credentials in the environment block. It is pretty concise,
stage("my aws stage") {
environment {
NOOP_CREDS_VAR = credentials('my-example-creds')
}
steps {
sh 'env'
sh 'aws sts get-caller-identity'
}
}
The environment block syntax requires the actual assignment of creds to a variable, hence NOOP_CREDS_VAR
On aside note. This issue is a glaring security hole and prevents the plugin adoption