Skip to content

Commit

Permalink
Support Multi Threaded and Distributed Environment
Browse files Browse the repository at this point in the history
Support Multi Threaded and Distributed Environment
  • Loading branch information
sameershaikahamed committed Mar 13, 2024
1 parent 4a7efda commit efdbd3d
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 202 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2024-03-11
- Support for multi-threaded and distributed environment.

## [1.0.18] - 2024-02-02
- Update to support JWKS public key re-generation.

Expand Down
16 changes: 14 additions & 2 deletions Jenkinsfile.Internal
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ if (params.MODE == "PROMOTE") {
// Anything added to assetDirectory will be attached to the Github Release

// assetDirectory is on the infrapool node.


env.ASSET_DIR=assetDirectory

target_version = targetVersion

// Build plugin with target version
Expand All @@ -28,7 +30,10 @@ if (params.MODE == "PROMOTE") {
infrapool.agentSh('scripts/build.sh')

// Copy built plugin to assetDirectory, so it will be attached to the Github Release
infrapool.agentSh("sudo cp target/*.hpi \"${assetDirectory}\"")
//infrapool.agentSh("sudo cp target/*.hpi \"${assetDirectory}\"")

infrapool.agentSh "ASSET_DIR=\"${assetDirectory}\" summon ./publish.sh"


// Do upstream release. This pushes plugin to jenkinsci project maven
// and the plugin index.
Expand Down Expand Up @@ -136,6 +141,13 @@ pipeline {
infrapool.agentSh 'scripts/build.sh'
}
}
post {
always {
script {
infrapool.agentArchiveArtifacts artifacts: 'target/*.hpi'
}
}
}
}

stage('Release') {
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</properties>
<name>Conjur Secrets Plugin</name>
<description>Plugin to retrieve secrets from CyberArk/Conjur</description>
<version>1.0.18</version>
<version>2.0.0</version>
<url>https://github.com/jenkinsci/conjur-credentials-plugin</url>
<!-- The default licence for Jenkins OSS Plugins is MIT. Substitute for the
applicable one if needed. -->
Expand Down
46 changes: 46 additions & 0 deletions publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash

# Publish a pre-release to artifactory

set -euo pipefail

# Load docker_rt function from utils.sh
# shellcheck source=/dev/null
. "$(dirname "${0}")/utils.sh"

# shellcheck disable=SC2012
target_package="$(ls -1tr target/*.hpi |tail -n 1)"

# Copy built hpi to ASSET_DIR so it will be attached to the Github Release

if [[ -n "${ASSET_DIR:-}" ]] && [[ -d "${ASSET_DIR:-}" ]]; then
echo "Copying ${target_package} to Asset Dir: ${ASSET_DIR}"
cp target/*.hpi "${ASSET_DIR}"
else
echo "ASSET_DIR is unset, unable to copy ${target_package} to ASSET_DIR for github release. ❌"
exit 1
fi

mkdir -p maven_cache

if [[ "${MODE:-}" == "PROMOTE" ]]; then
echo "PROMOTE build, publishing to internal artifactory and ossrh (maven central)"
maven_profiles="artifactory,ossrh,sign"
else
echo "Release build, publishing to internal artifactory"
maven_profiles="artifactory,sign"
fi

docker run \
-e OSSRH_USERNAME \
-e OSSRH_PASSWORD \
-e JFROG_USERNAME \
-e JFROG_APIKEY \
--volume "${PWD}:${PWD}" \
--volume "${PWD}/maven_cache":/root/.m2 \
--volume "$GPG_PASSWORD:/gpg_password" \
--volume "$GPG_PRIVATE_KEY:/gpg_key" \
--workdir "${PWD}" \
tools \
/bin/bash -ec "gpg --batch --passphrase-file /gpg_password --trust-model always --import /gpg_key
mvn --batch-mode --settings mvn-settings.xml --file pom.xml deploy -Dmaven.test.skip -P ${maven_profiles}"
92 changes: 62 additions & 30 deletions src/main/java/org/conjur/jenkins/api/ConjurAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,24 @@
import okhttp3.Response;

/**
* The ConjurAPI class authenticates the request based on the configuration and
* retrieves secret from Conjur Vault
* The ConjurAPI class provides the service to authenticate and retrieve secrets
* based on API Key/JWT authentication using the Conjur Configuration details
* configured either through the Jenkins Global configuration form or as
* environment. The request to authenticate (API Key/JWT) will be processed in
* Conjur Server and return authorised(200-OK) or unauthorised code
* (401-UnAuthorized) code. The request to fetch the secrets based on the
* credetnialID will be processed only if the authentication is successful. Upon
* successful authentication , the request to fetch the secret is processed and
* returns secrets if available. The request to fetch secrets first checks if
* the credentialId is available and having grant permission based on identity
* If CredentialID is not found ,returns <b>Credential NotFound message</b>. If
* CredentialID does not have permission , returns <b>401 UnAuthorized
* message</b>. If secrets not available for the CredentialID ,returns
* <b>Credential ID is empty message</b>.
*/
public class ConjurAPI {
/**
* static constructor to set the Conjur Auth Configuration Info
* Inner class to include the Conjur Configuration Parameters
*/
public static class ConjurAuthnInfo {
public String applianceUrl;
Expand All @@ -54,7 +66,15 @@ public static class ConjurAuthnInfo {

private static final Logger LOGGER = Logger.getLogger(ConjurAPI.class.getName());

private static void defaultToEnvironment(ConjurAuthnInfo conjurAuthn) {
/**
* Set the ConjurAuthnInfo with the environment variables
*
* @param conjurAuthn
*/

private static void defaultToEnvironment(ConjurAuthnInfo conjurAuthn) {
LOGGER.log(Level.FINE, "Start of defaultToEnvironment()");

Map<String, String> env = System.getenv();
if (conjurAuthn.applianceUrl == null && env.containsKey("CONJUR_APPLIANCE_URL"))
conjurAuthn.applianceUrl = env.get("CONJUR_APPLIANCE_URL");
Expand All @@ -64,21 +84,27 @@ private static void defaultToEnvironment(ConjurAuthnInfo conjurAuthn) {
conjurAuthn.login = env.get("CONJUR_AUTHN_LOGIN");
if (conjurAuthn.apiKey == null && env.containsKey("CONJUR_AUTHN_API_KEY"))
conjurAuthn.apiKey = env.get("CONJUR_AUTHN_API_KEY");
LOGGER.log(Level.FINE, "End of defaultToEnvironment()");
}

/**
* Retrieves the authorization token based on the Configuration.
* Method to build the client authentication API Key/JWT request based on the
* ConjurConfiguration.
*
* @param OkHttpClient client to process request
* @param ConjurConfiguration with configuration info
* @param Jenkins ModelObject context
* @return AuthorizationToken to authenticate the request
* @throws IOException
* @param client OkHttp builds HTTP/HTTP/2 client that shares the same
* connection,thread pool and configuration.
* @param configuration ConjurConfiguration object containing
* account,applianceUrl,credentialID,certificateCredentialID,ownerFullName.
* @param context current context in which Jenkins Job are running
* @return status code to 200-OK if request is authenticated or 401 if
* Unauthorized
* @throws IOException in case of error connecting to Conjur Server
*/
@SuppressFBWarnings
public static String getAuthorizationToken(OkHttpClient client, ConjurConfiguration configuration,
public static String getAuthorizationToken(OkHttpClient client, ConjurConfiguration configuration,
ModelObject context) throws IOException {
LOGGER.log(Level.FINE,
LOGGER.log(Level.FINE, "Start of getAuthorizationToken()");
LOGGER.log(Level.INFO,
"getAuthorizationToken input params" + "Client:" + client + "Configuration:" + configuration);
String resultingToken = null;

Expand All @@ -103,12 +129,13 @@ public static String getAuthorizationToken(OkHttpClient client, ConjurConfigurat

Request request = null;
if (conjurAuthn.login != null && conjurAuthn.apiKey != null) {
LOGGER.log(Level.FINE, "Authenticating with Conjur (authn)");
LOGGER.log(Level.FINE, "Creating authentication request for API Key authentication with Conjur");
request = new Request.Builder()
.url(String.format("%s/%s/%s/%s/authenticate", conjurAuthn.applianceUrl, conjurAuthn.authnPath,
conjurAuthn.account, URLEncoder.encode(conjurAuthn.login, "utf-8")))
.post(RequestBody.create(MediaType.parse("text/plain"), conjurAuthn.apiKey)).build();
} else if (conjurAuthn.authnPath != null && conjurAuthn.apiKey != null) {
LOGGER.log(Level.FINE, "Creating authentication request for JWT authentication with Conjur");
String authnPath = conjurAuthn.authnPath.indexOf("/") == -1 ? "authn-jwt/" + conjurAuthn.authnPath
: conjurAuthn.authnPath;
LOGGER.log(Level.FINE, "Authenticating with Conjur (JWT) authnPath={0}", authnPath);
Expand All @@ -126,11 +153,12 @@ public static String getAuthorizationToken(OkHttpClient client, ConjurConfigurat
LOGGER.log(Level.FINEST,
() -> "Conjur Authenticate response " + response.code() + " - " + response.message());
if (response.code() != 200) {

throw new IOException("Error authenticating to Conjur [" + response.code() + " - " + response.message()
+ "\n" + resultingToken);
}
} else {
LOGGER.log(Level.FINE, "Failed to find credentials for conjur authentication");
LOGGER.log(Level.FINE, "Failed to authenticate with conjur server");
}
return resultingToken;
}
Expand All @@ -143,7 +171,7 @@ public static String getAuthorizationToken(OkHttpClient client, ConjurConfigurat
* @param Jenkins ModelObject context
* @return ConjurAuthnInfo
*/
public static ConjurAuthnInfo getConjurAuthnInfo(ConjurConfiguration configuration,
public static ConjurAuthnInfo getConjurAuthnInfo(ConjurConfiguration configuration,
List<UsernamePasswordCredentials> availableCredentials, ModelObject context) {
LOGGER.log(Level.FINE, "Start of getConjurAuthnInfo()");
ConjurAuthnInfo conjurAuthn = new ConjurAuthnInfo();
Expand Down Expand Up @@ -178,7 +206,8 @@ public static ConjurAuthnInfo getConjurAuthnInfo(ConjurConfiguration configurati
return conjurAuthn;
}

private static void setConjurAuthnForJITCredentialAccess(ModelObject context, ConjurAuthnInfo conjurAuthn) {
private static void setConjurAuthnForJITCredentialAccess(ModelObject context,
ConjurAuthnInfo conjurAuthn) {
LOGGER.log(Level.FINE, "Start of setConjurAuthnForJITCredentialAccess()");
String token = JwtToken.getToken(context);
GlobalConjurConfiguration globalconfig = GlobalConfiguration.all().get(GlobalConjurConfiguration.class);
Expand All @@ -192,23 +221,24 @@ private static void setConjurAuthnForJITCredentialAccess(ModelObject context, Co
}

/**
* Retrieves the secret for the key
* This method gets the {@link ConjurAuthIno} data and retrieve the secret for the valid authenticationToken,account
* variablePath. The request to fetch the secret are build using the OkHttp client.
*
* @param OkHttpClient to process request
* @param Jenkins configuration contianing the Conjur Config details
* @param Authorization token to authenticate the request
* @param client OkHttp builds HTTP/HTTP/2 client that shares the same connection,thread pool and configuration.
* @param configuration {@link ConjurConfiguration} containing the Conjur authentication parameters
* @param authToken token to authenticate the request.
* @param variablePath for which to retrieve the secrets
* @return the secrets for the specified variablePath
* @throws IOException
*/
@SuppressFBWarnings
public static String getSecret(OkHttpClient client, ConjurConfiguration configuration, String authToken,
String variablePath) throws IOException {
public static String getSecret(OkHttpClient client, ConjurConfiguration configuration,
String authToken, String variablePath) throws IOException {
LOGGER.log(Level.FINE, "Start of getSecret()");
long start = System.nanoTime();

ConjurAuthnInfo conjurAuthn = getConjurAuthnInfo(configuration, null, null);

LOGGER.log(Level.FINEST, "Fetching secret from Conjur");
LOGGER.log(Level.FINEST, "Fetching secret from Conjur Server");
Request request = new Request.Builder().url(
String.format("%s/secrets/%s/variable/%s", conjurAuthn.applianceUrl, conjurAuthn.account, variablePath))
.get().addHeader("Authorization", "Token token=\"" + authToken + "\"").build();
Expand All @@ -221,16 +251,17 @@ public static String getSecret(OkHttpClient client, ConjurConfiguration configur
throw new IOException("Error fetching secret from Conjur [" + response.code() + " - " + response.message()
+ "\n" + result);
}
long end = System.nanoTime();

LOGGER.log(Level.FINE, "End of getSecret()");
return result;
}

/**
* Log the Conjur Configuration details
*
* @param to log the ConjurConfiguration from Jenkins configuration
* @return the Conjur Configuration parameters
* @param conjurConfiguration log the ConjurConfiguration from Jenkins
* configuration
* @return ConjurConfiguration log the Conjur Configuration parameters
*/
public static ConjurConfiguration logConjurConfiguration(ConjurConfiguration conjurConfiguration) {
LOGGER.log(Level.FINE, "Start of logConjurConfiguration()");
Expand All @@ -244,7 +275,7 @@ public static ConjurConfiguration logConjurConfiguration(ConjurConfiguration con
return conjurConfiguration;
}

private static void initializeWithCredential(ConjurAuthnInfo conjurAuthn, String credentialID,
private static void initializeWithCredential(ConjurAuthnInfo conjurAuthn, String credentialID,
List<UsernamePasswordCredentials> availableCredentials) {
LOGGER.log(Level.FINE, "Start of initializeWithCredential()");
if (credentialID != null && !credentialID.isEmpty()) {
Expand All @@ -267,7 +298,8 @@ private static void initializeWithCredential(ConjurAuthnInfo conjurAuthn, String
* @return the Conjur Configuration based on the Jenkins ModelOjbect
*/

public static ConjurConfiguration getConfigurationFromContext(ModelObject context, ModelObject storeContext) {
public static ConjurConfiguration getConfigurationFromContext(ModelObject context,
ModelObject storeContext) {
LOGGER.log(Level.FINE, "Start of getConfigurationFromContext()");
ModelObject effectiveContext = context != null ? context : storeContext;

Expand Down Expand Up @@ -307,7 +339,7 @@ public static ConjurConfiguration getConfigurationFromContext(ModelObject contex
}

@SuppressWarnings("unchecked")
private static ConjurConfiguration inheritedConjurConfiguration(Item job) {
private static ConjurConfiguration inheritedConjurConfiguration(Item job) {
LOGGER.log(Level.FINE, "Start of inheritedConjurConfiguration()");
for (ItemGroup<? extends Item> g = job != null ? job.getParent()
: null; g instanceof AbstractFolder; g = ((AbstractFolder<? extends Item>) g).getParent()) {
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/org/conjur/jenkins/api/ConjurAPIUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
* ConjurAPIUtils class used to build the OkHttp Client object and create
* CertificateCredentials.
*
* @author Jaleela.FaizurRahman
*
*/
public class ConjurAPIUtils {
Expand All @@ -42,7 +41,7 @@ public class ConjurAPIUtils {
* @param ConjurConfiguration configuration
* @return CertificateCredentials
*/
static CertificateCredentials certificateFromConfiguration(ConjurConfiguration configuration) {
static synchronized CertificateCredentials certificateFromConfiguration(ConjurConfiguration configuration) {
LOGGER.log(Level.FINE, "Start of certificateFromConfiguration()");

CertificateCredentials certificate = null;
Expand All @@ -67,7 +66,8 @@ static CertificateCredentials certificateFromConfiguration(ConjurConfiguration c
* @return OkHttpClient clientf
*/

static OkHttpClient httpClientWithCertificate(CertificateCredentials certificate) {
static synchronized OkHttpClient httpClientWithCertificate(CertificateCredentials certificate) {
LOGGER.log(Level.FINE, "Start of httpClientWithCertificate()");
OkHttpClient client = null;

try {
Expand Down Expand Up @@ -96,7 +96,7 @@ static OkHttpClient httpClientWithCertificate(CertificateCredentials certificate
} catch (Exception e) {
throw new IllegalArgumentException("Error configuring server certificates.", e);
}

LOGGER.log(Level.FINE, "End of httpClientWithCertificate()");
return client;

}
Expand All @@ -107,14 +107,15 @@ static OkHttpClient httpClientWithCertificate(CertificateCredentials certificate
* @param ConjurConfiguration configuration
* @return OkHttpClient client
*/
public static OkHttpClient getHttpClient(ConjurConfiguration configuration) {
public static synchronized OkHttpClient getHttpClient(ConjurConfiguration configuration) {
LOGGER.log(Level.FINE, "Start of getHttpClient()");

CertificateCredentials certificate = certificateFromConfiguration(configuration);

if (certificate != null) {
return httpClientWithCertificate(certificate);
}

LOGGER.log(Level.FINE, "End of getHttpClient()");
return new OkHttpClient.Builder().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
* implements Serializable Retrieves the Conjur configuration details and assign
* to Configuration parameters
*
* @author Jaleela.FaizurRahman
*
*/

Expand All @@ -53,7 +52,6 @@ public class ConjurConfiguration extends AbstractDescribableImpl<ConjurConfigura
/**
* Inner static class to retrieve the configuration details from Jenkins
*
* @author Jaleela.FaizurRahman
*
*/
@Extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
/**
* ConjurJITJobProperty DTO class to set the ConjurConfiguration
*
* @author Jaleela.FaizurRahman
*
* @param extends Jenkins JobProperty
*/
Expand Down
Loading

0 comments on commit efdbd3d

Please sign in to comment.