Skip to content

Commit ce96be0

Browse files
authored
Add support for secret file credentials (#246)
* add support for secret file type of credentials * Add documentation * adjust the default fileName of credentials of type secret-file
1 parent 187b419 commit ce96be0

File tree

4 files changed

+143
-8
lines changed

4 files changed

+143
-8
lines changed

README.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The plugin acts as an Azure Active Directory Application and must be configured
1515

1616
In the Jenkins **Configure System** page, configure the following two options in the **Azure Key Vault Plugin** section
1717
* **Key Vault URL** - The url where your Key Vault resides (e.g. `https://myvault.vault.azure.net/`)
18-
* **Credential ID** - The ID associated with a secret in the Jenkins secret store. Supported types are:
18+
* **Credential ID** - The ID associated with a secret in the Jenkins secret store. Supported types are:
1919
- **Azure Service Principal**
2020
- **Azure Managed Identity** (both user and system assigned)
2121

@@ -144,7 +144,7 @@ node {
144144
With overrides:
145145
```groovy
146146
static LinkedHashMap<String, Object> secret(String secretName, String envVar) {
147-
[
147+
[
148148
secretType: 'Secret',
149149
name: secretName,
150150
version: '342432lkjhdasjld',
@@ -158,7 +158,7 @@ node {
158158
]
159159
160160
withAzureKeyvault(
161-
azureKeyVaultSecrets: secrets,
161+
azureKeyVaultSecrets: secrets,
162162
keyVaultURLOverride: 'https://mykeyvault.vault.azure.net',
163163
credentialIDOverride: 'service-principal'
164164
) {
@@ -199,8 +199,8 @@ pipeline {
199199
stage('Build') {
200200
options {
201201
azureKeyVault(
202-
credentialID: 'my-sp',
203-
keyVaultURL: 'https://my.vault.azure.net',
202+
credentialID: 'my-sp',
203+
keyVaultURL: 'https://my.vault.azure.net',
204204
secrets: [
205205
[envVariable: 'MY_SECRET', name: 'my-secret', secretType: 'Secret']
206206
]
@@ -254,9 +254,13 @@ To use a different type add a tag called `type` with one of the below values:
254254
- `string` - Secret text
255255
- `username` - Username with password
256256
- add a tag `username` for the username of the credential
257+
- `secretFile` - a file with secret content
258+
- (optional) add a tag `fileName` for the secret file name, when it is fetched. Default is `${secretNameIntheVault}.txt`.
257259
- `sshUserPrivateKey` - SSH Private key
258260
- add a tag `username` for the username of the credential
259-
- (optional) add a tag `username-is-secret` and set it to true to hide the username in the build logs
261+
- (optional) add a tag `username-is-secret` and set it to true to hide the username in the build logs
262+
263+
#### Secret String
260264

261265
Declarative Pipeline:
262266

@@ -295,7 +299,7 @@ az keyvault secret set --vault-name my-vault \
295299
--tags username=github-user type=username
296300
```
297301

298-
Scripted Pipeline:
302+
Scripted Pipeline:
299303
```groovy
300304
job('my example') {
301305
scm {
@@ -309,6 +313,26 @@ job('my example') {
309313
}
310314
```
311315

316+
#### Secret file
317+
318+
```bash
319+
az keyvault secret set --vault-name my-vault \
320+
--name a-secret-file-vault-secret \
321+
--value "-----BEGIN test secretFile-----\nline 1\nline2\nbla\nblob" \
322+
--tags type=secretFile fileName=mySecretFile.txt
323+
```
324+
325+
Scripted Pipeline:
326+
```groovy
327+
node {
328+
withCredentials([
329+
file(credentialsId: "test-secretFile-credentialsId", variable: "VARIABLE_CONTAINING_PATH_TO_SECRET_FILE")]) {
330+
sh("doSomething --use-this-secret-file \$VARIABLE_CONTAINING_PATH_TO_SECRET_FILE")
331+
}
332+
}
333+
```
334+
335+
312336
#### SSH Username with private key
313337

314338
```bash
@@ -381,7 +405,7 @@ If the passphrase can not be found in the vault, the secret will not load and a
381405

382406
You can filter which secrets are visible to the credentials provider.
383407
By default, the plugin will load all secrets stored within the Key Vault.
384-
However, your Key Vault may be the Secret Source for multiple applications, or contains secrets not needed directly by Jenkins.
408+
However, your Key Vault may be the Secret Source for multiple applications, or contains secrets not needed directly by Jenkins.
385409
To filter out secrets from being set, add a System Property or Environment Variable:
386410

387411
**Via System Property**:

src/main/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureCredentialsProvider.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import jenkins.model.Jenkins;
3939
import org.acegisecurity.Authentication;
4040
import org.apache.commons.lang3.StringUtils;
41+
import org.jenkinsci.plugins.azurekeyvaultplugin.credentials.secretfile.AzureSecretFileCredentials;
4142
import org.jenkinsci.plugins.azurekeyvaultplugin.credentials.sshuserprivatekey.AzureSSHUserPrivateKeyCredentials;
4243
import org.jenkinsci.plugins.azurekeyvaultplugin.credentials.string.AzureSecretStringCredentials;
4344
import org.jenkinsci.plugins.azurekeyvaultplugin.credentials.usernamepassword.AzureUsernamePasswordCredentials;
@@ -189,6 +190,15 @@ private static Collection<IdCredentials> fetchCredentials() {
189190
credentials.add(cred);
190191
break;
191192
}
193+
case "secretFile": {
194+
String fileName = tags.get("fileName");
195+
if(fileName.isEmpty()){
196+
fileName = getSecretName(id) + ".txt";
197+
}
198+
AzureSecretFileCredentials cred = new AzureSecretFileCredentials(scope, jenkinsID, description, fileName, new KeyVaultSecretRetriever(client, id));
199+
credentials.add(cred);
200+
break;
201+
}
192202
case "username": {
193203
AzureUsernamePasswordCredentials cred = new AzureUsernamePasswordCredentials(
194204
scope, jenkinsID, tags.get("username"), description, new KeyVaultSecretRetriever(client, id)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.jenkinsci.plugins.azurekeyvaultplugin.credentials.secretfile;
2+
3+
import com.cloudbees.plugins.credentials.CredentialsProvider;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
5+
import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
6+
import edu.umd.cs.findbugs.annotations.NonNull;
7+
import hudson.Extension;
8+
import hudson.util.Secret;
9+
import java.io.ByteArrayInputStream;
10+
import java.io.InputStream;
11+
import java.util.function.Supplier;
12+
import org.jenkinsci.plugins.azurekeyvaultplugin.AzureCredentialsProvider;
13+
import org.jenkinsci.plugins.plaincredentials.FileCredentials;
14+
import org.jenkinsci.plugins.plaincredentials.impl.Messages;
15+
import org.jvnet.localizer.ResourceBundleHolder;
16+
17+
public class AzureSecretFileCredentials extends BaseStandardCredentials implements FileCredentials {
18+
19+
@NonNull
20+
private final String fileName;
21+
22+
@NonNull
23+
private final Supplier<Secret> secretBytes;
24+
25+
public AzureSecretFileCredentials(
26+
CredentialsScope scope,
27+
String id,
28+
String description,
29+
String fileName,
30+
Supplier<Secret> secretBytes
31+
) {
32+
super(scope, id, description);
33+
this.fileName = fileName;
34+
this.secretBytes = secretBytes;
35+
}
36+
37+
@NonNull
38+
@Override
39+
public String getFileName() {
40+
return fileName;
41+
}
42+
43+
@NonNull
44+
@Override
45+
public InputStream getContent() throws java.io.UnsupportedEncodingException {
46+
String fileContent = Secret.toString(getSecretBytes());
47+
return new ByteArrayInputStream(fileContent.getBytes("UTF-8"));
48+
}
49+
50+
public Secret getSecretBytes() {
51+
return secretBytes.get();
52+
}
53+
54+
@Extension
55+
@SuppressWarnings("unused")
56+
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {
57+
@Override
58+
@NonNull
59+
public String getDisplayName() {
60+
return ResourceBundleHolder.get(Messages.class).format("FileCredentialsImpl.secret_file");
61+
}
62+
63+
@Override
64+
public boolean isApplicable(CredentialsProvider provider) {
65+
return provider instanceof AzureCredentialsProvider;
66+
}
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.jenkinsci.plugins.azurekeyvaultplugin.credentials.secretfile;
2+
3+
import com.cloudbees.plugins.credentials.CredentialsSnapshotTaker;
4+
import hudson.Extension;
5+
import hudson.util.Secret;
6+
import org.jenkinsci.plugins.azurekeyvaultplugin.credentials.Snapshot;
7+
8+
@Extension
9+
@SuppressWarnings("unused")
10+
public class AzureSecretFileCredentialsSnapshotTaker extends CredentialsSnapshotTaker<AzureSecretFileCredentials> {
11+
@Override
12+
public Class<AzureSecretFileCredentials> type() {
13+
return AzureSecretFileCredentials.class;
14+
}
15+
16+
@Override
17+
public AzureSecretFileCredentials snapshot(AzureSecretFileCredentials credential) {
18+
SecretSnapshot secretSnapshot = new SecretSnapshot(credential.getSecretBytes());
19+
return new AzureSecretFileCredentials(
20+
credential.getScope(),
21+
credential.getId(),
22+
credential.getDescription(),
23+
credential.getFileName(),
24+
secretSnapshot
25+
);
26+
}
27+
28+
private static class SecretSnapshot extends Snapshot<Secret> {
29+
SecretSnapshot(Secret value) {
30+
super(value);
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)