Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ When registering the approle backend you can set a couple of different parameter
This is just a short introduction, please refer to [Hashicorp itself](https://www.vaultproject.io/docs/auth/approle.html) to get detailed information.
### What about other backends?
Hashicorp explicitly recommends the AppRole Backend for machine-to-machine authentication. Token based auth is mainly supported for backwards compatibility.
Other backends that might make sense are the AWS EC2 backend, the Azure backend, and the Kubernetes backend. But we do not support these yet. Feel free to contribute!
Other backends that might make sense are the the Azure backend, and the Kubernetes backend. But we do not support these yet. Feel free to contribute!

Implementing additional authentication backends is actually quite easy:

Expand Down Expand Up @@ -67,6 +67,14 @@ You enter your github personal access token to authenticate to vault.

You enter your Vault GCP auth `role` name and `audience`. The JWT will be automatically retrieved from GCE metdata. This requires that Jenkins master is running on a GCE instance.

#### Vault AWS EC2 Credential

![AWS EC2 Credential](docs/images/aws_ec2_credential.png)

You enter your Vault AWS auth `role` name and `mountPath` (default: aws). The pkcs7 will be retrieved from AWS Instance Metadata server (IMDS) and used for authentication. This requires that Jenkins master is running on an AWS instance.

Note: IMDSv1 is used to fetch the pkcs7. For more details about [IMDS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)

#### Vault Kubernetes Credential

![Kubernetes Credential](docs/images/kubernetes_credential.png)
Expand Down
Binary file added docs/images/aws_ec2_credential.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.datapipe.jenkins.vault.credentials;

import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultException;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.datapipe.jenkins.vault.exception.VaultPluginException;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import org.kohsuke.stapler.DataBoundConstructor;

public class VaultAWSCredential extends AbstractVaultTokenCredential {

private static final String METADATA_HTTP_SCHEME = "http";
private static final String METADATA_HOST_ADDRESS = "169.254.169.254";
private static final String METADATA_PATH = "/latest/dynamic/instance-identity/pkcs7";

@NonNull
private final String role;
@NonNull
private final String awsMountPath;
@NonNull
private final String nonce;

@DataBoundConstructor
public VaultAWSCredential(
@CheckForNull CredentialsScope scope,
@CheckForNull String id,
@CheckForNull String description,
@NonNull String role,
@NonNull String awsMountPath,
@NonNull String nonce) {
super(scope, id, description);
this.role = role;
this.awsMountPath = awsMountPath;
this.nonce = nonce;
}

@NonNull
public String getRole() {
return role;
}

@Override
public String getToken(Vault vault) {
String pkcs7;
try {
pkcs7 = retrieveAWSPKCS7();
} catch (URISyntaxException | IOException e) {
throw new VaultPluginException("could not get pkcs7 from AWS metadata", e);
}

try {
return vault.withRetries(5, 500).auth().loginByAwsEc2(role, pkcs7, nonce, awsMountPath).getAuthClientToken();
} catch (VaultException e) {
throw new VaultPluginException("could not log in into vault", e);
}
}

@Extension
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {

@NonNull
@Override
public String getDisplayName() {
return "Vault AWS EC2 Credential";
}

}

private String retrieveAWSPKCS7() throws URISyntaxException, IOException {
URL url = new URI(METADATA_HTTP_SCHEME, METADATA_HOST_ADDRESS, METADATA_PATH, null).toURL();
URLConnection connection = url.openConnection();
HttpURLConnection httpConnection = safelyCastToHttpUrlConnection(connection);

return download(httpConnection);
}

private static HttpURLConnection safelyCastToHttpUrlConnection(URLConnection connection) {
if (connection instanceof HttpURLConnection) {
return (HttpURLConnection) connection;
} else {
throw new RuntimeException("We do not have Http connection, but we used http schema");
}
}

private static String download(URLConnection connection) throws IOException {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
in.lines().forEachOrdered(sb::append);
return sb.toString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler">
<f:entry title="Role">
<f:textbox field="role" name="role"/>
</f:entry>
<f:entry title="Nonce">
<f:password field="nonce" name="nonce"/>
</f:entry>
<f:entry title="MountPath">
<f:textbox field="awsMountPath" name="mountPath" default="aws"/>
</f:entry>
<st:include page="id-and-description" class="${descriptor.clazz}"/>
</j:jelly>