diff --git a/clients/java/zts/examples/tls-support/pom.xml b/clients/java/zts/examples/tls-support/pom.xml index be80ea1669e..f8c06848921 100644 --- a/clients/java/zts/examples/tls-support/pom.xml +++ b/clients/java/zts/examples/tls-support/pom.xml @@ -27,7 +27,7 @@ UTF-8 UTF-8 - 1.7.33 + 1.7.35 diff --git a/clients/java/zts/examples/tls-support/src/main/java/com/yahoo/athenz/example/zts/tls/client/ZTSAWSCredsClient.java b/clients/java/zts/examples/tls-support/src/main/java/com/yahoo/athenz/example/zts/tls/client/ZTSAWSCredsClient.java index a5f909a3028..3a9872e78b0 100644 --- a/clients/java/zts/examples/tls-support/src/main/java/com/yahoo/athenz/example/zts/tls/client/ZTSAWSCredsClient.java +++ b/clients/java/zts/examples/tls-support/src/main/java/com/yahoo/athenz/example/zts/tls/client/ZTSAWSCredsClient.java @@ -29,6 +29,7 @@ import com.yahoo.athenz.zts.PublicKeyEntry; import com.yahoo.athenz.zts.ZTSClient; +import com.yahoo.athenz.zts.AWSCredentialsProviderImpl; import com.yahoo.athenz.zts.ZTSClientException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; @@ -66,19 +67,24 @@ public static void main(String[] args) { SSLContext sslContext = Utils.buildSSLContext(keyRefresher.getKeyManagerProxy(), keyRefresher.getTrustManagerProxy()); - // we must not close this client as long as we're using the - // AWS credentials provider since it needs this client to - // refresh the certs when required + // obtain temporary credential provider for our domain and role - ZTSClient ztsClient = new ZTSClient(ztsUrl, sslContext); + AWSCredentialsProviderImpl awsCredProvider = new AWSCredentialsProviderImpl(ztsUrl, + sslContext, domainName, roleName); - // retrieve and display aws temporary creds + // retrieve and display aws temporary creds. Typically you just pass + // the AWSCredentialsProvider object to any AWS api that requires it. + // for example, when creating an AWS S3 client + // AmazonS3 s3client = AmazonS3ClientBuilder.standard() + // .withCredentials(awsCredProvider).withClientConfiguration(cltConf) + // .withRegion(getRegion()).build(); + + retrieveAWSTempCreds(awsCredProvider); - retrieveAWSTempCreds(ztsClient, domainName, roleName); - - // we're done with our provider so we can close our client + // once we're done with our api and we no longer need our + // provider we need to make sure to close it - ztsClient.close(); + awsCredProvider.close(); } catch (Exception ex) { System.out.println("Exception: " + ex.getMessage()); @@ -87,19 +93,28 @@ public static void main(String[] args) { } } - private static boolean retrieveAWSTempCreds(ZTSClient ztsClient, final String domainName, - final String roleName) { + private static boolean retrieveAWSTempCreds(AWSCredentialsProvider awsCredProvider) { try { - AWSCredentialsProvider awsCredProvider = ztsClient.getAWSCredentialProvider(domainName, roleName); - AWSCredentials awsCreds = awsCredProvider.getCredentials(); - if (awsCreds == null) { - System.out.println("Error: AWS Credentials are not available"); - return false; + // just for testing purposes we're going to run this code + // for 2 hours and keep asking for credentials every minute + // to make sure zts client is caching the creds and giving + // us new ones when they're about to expire + + for (int i = 0; i < 120; i++) { + AWSCredentials awsCreds = awsCredProvider.getCredentials(); + if (awsCreds == null) { + System.out.println("Error: AWS Credentials are not available"); + return false; + } + System.out.println("AWS Temporary Credentials:\n"); + System.out.println("\tAccess Key Id : " + awsCreds.getAWSAccessKeyId()); + System.out.println("\tSecret Key : " + awsCreds.getAWSSecretKey()); + try { + Thread.sleep(60000); + } catch (InterruptedException ex) { + } } - System.out.println("AWS Temporary Credentials:\n"); - System.out.println("\tAccess Key Id : " + awsCreds.getAWSAccessKeyId()); - System.out.println("\tSecret Key : " + awsCreds.getAWSSecretKey()); } catch (ZTSClientException ex) { System.out.println("Unable to retrieve AWS credentials: " + ex.getMessage()); return false; diff --git a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/AWSCredentialsProviderImpl.java b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/AWSCredentialsProviderImpl.java index 2deab522a18..fb9fbccc2ce 100644 --- a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/AWSCredentialsProviderImpl.java +++ b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/AWSCredentialsProviderImpl.java @@ -1,50 +1,107 @@ +/** + * Copyright 2017 Yahoo Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.yahoo.athenz.zts; +import java.io.Closeable; +import java.io.IOException; +import javax.net.ssl.SSLContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.BasicSessionCredentials; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -public class AWSCredentialsProviderImpl implements AWSCredentialsProvider { +public class AWSCredentialsProviderImpl implements AWSCredentialsProvider, Closeable { + private static final Logger LOG = LoggerFactory.getLogger(AWSCredentialsProviderImpl.class); - private static String athensSvcDomain = ""; - private static String athensDomRole = ""; + + private String domainName; + private String roleName; + private ZTSClient ztsClient; private volatile AWSCredentials credentials; - private ZTSClient ztsClt; - + private boolean closeZTSClient; + + public AWSCredentialsProviderImpl(ZTSClient ztsClient, String domainName, String roleName) { + this.ztsClient = ztsClient; + this.domainName = domainName; + this.roleName = roleName; + this.closeZTSClient = false; + } - public AWSCredentialsProviderImpl(ZTSClient ztsClt, String athensSvcDomain, String athensDomRole) { - this.ztsClt = ztsClt; - this.athensDomRole = athensDomRole; - this.athensSvcDomain = athensSvcDomain; + /** + * Constructs a new AWSCredentialsProvider object with the given SSLContext object, + * ZTS Server Url, Athenz domain name and AWS Role Name to retrieve temporary + * credentials for. The constructor will automatically create and use the ZTS + * client object for retrieving credentials. This object must be closed so + * the ZTS client object is closed as well. + * @param ztsUrl ZTS Server's URL + * @param sslContext SSLContext that includes service's private key and x.509 certificate + * for authenticating requests + * @param domainName name of the domain + * @param roleName is the name of the role + */ + public AWSCredentialsProviderImpl(String ztsUrl, SSLContext sslContext, + String domainName, String roleName) { + this.domainName = domainName; + this.roleName = roleName; + this.ztsClient = new ZTSClient(ztsUrl, sslContext); + this.closeZTSClient = true; } + @Override + public void close() throws IOException { + if (closeZTSClient) { + ztsClient.close(); + } + } + @Override public AWSCredentials getCredentials() { - this.refresh(); - return this.credentials; + + // we are going to first refresh our credentials object. + // for initial request this will fetch the credentials + // while for others it will check if it exists in the cache + // and only fetch if it's about to expire + + refresh(); + return credentials; } @Override public void refresh() { try { - AWSTemporaryCredentials creds = ztsClt.getAWSTemporaryCredentials(athensSvcDomain, athensDomRole); + AWSTemporaryCredentials creds = ztsClient.getAWSTemporaryCredentials(domainName, roleName); if (LOG.isDebugEnabled()) { - LOG.debug("AWSCredentialsProviderImpl:refresh: Credentials with id: \"" + creds.accessKeyId + "\" were fetched"); + LOG.debug("Refresh: Credentials with id: {} and expiration {} were fetched", + creds.getAccessKeyId(), creds.getExpiration()); } this.credentials = new BasicSessionCredentials( creds.getAccessKeyId(), creds.getSecretAccessKey(), creds.getSessionToken()); - } catch (ZTSClientException exp) { - this.credentials = null; - LOG.error("AWSCredentialsProviderImpl:refresh: Failed to get the AWS temporary credentials from ZTS. Status: " + exp.getCode() + "Error" + exp.getData()); - } catch (Exception exp) { - this.credentials = null; - LOG.error("AWSCredentialsProviderImpl:refresh: Failed to refresh credentials . Error: " + exp.getMessage()); + + } catch (ZTSClientException ex) { + credentials = null; + LOG.error("Refresh: Failed to get the AWS temporary credentials from ZTS: {}", + ex.getMessage()); + } catch (Exception ex) { + credentials = null; + LOG.error("Refresh: Failed to refresh credentials: {}", ex.getMessage()); } } } diff --git a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ZTSClient.java b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ZTSClient.java index 03e3853ff4c..30f9aae0138 100644 --- a/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ZTSClient.java +++ b/clients/java/zts/src/main/java/com/yahoo/athenz/zts/ZTSClient.java @@ -78,6 +78,7 @@ public class ZTSClient implements Closeable { private String service = null; protected ZTSRDLGeneratedClient ztsClient; protected ServiceIdentityProvider siaProvider = null; + private SSLContext sslContext = null; // configurable fields // @@ -185,7 +186,7 @@ static boolean initConfigValues() { * milliseconds. */ public ZTSClient() { - initClient(null, null, null, null, null, null); + initClient(null, null, null, null, null); enablePrefetch = false; // can't use this domain and service for prefetch } @@ -204,7 +205,7 @@ public ZTSClient() { * @param ztsUrl ZTS Server's URL (optional) */ public ZTSClient(String ztsUrl) { - initClient(ztsUrl, null, null, null, null, null); + initClient(ztsUrl, null, null, null, null); enablePrefetch = false; // can't use this domain and service for prefetch } @@ -240,7 +241,7 @@ public ZTSClient(String ztsUrl, Principal identity) { if (identity.getAuthority() == null) { throw new IllegalArgumentException("Principal Authority cannot be null"); } - initClient(ztsUrl, identity, null, null, null, null); + initClient(ztsUrl, identity, null, null, null); enablePrefetch = false; // can't use this domain and service for prefetch } @@ -261,7 +262,8 @@ public ZTSClient(String ztsUrl, SSLContext sslContext) { if (sslContext == null) { throw new IllegalArgumentException("SSLContext object must be specified"); } - initClient(ztsUrl, null, null, null, null, sslContext); + this.sslContext = sslContext; + initClient(ztsUrl, null, null, null, null); } /** @@ -305,7 +307,7 @@ public ZTSClient(String ztsUrl, String domainName, String serviceName, if (siaProvider == null) { throw new IllegalArgumentException("Service Identity Provider must be specified"); } - initClient(ztsUrl, null, domainName, serviceName, siaProvider, null); + initClient(ztsUrl, null, domainName, serviceName, siaProvider); } /** @@ -456,7 +458,7 @@ static PrivateKeyStore loadServicePrivateKey() { } void initClient(String url, Principal identity, String domainName, String serviceName, - ServiceIdentityProvider siaProvider, SSLContext sslContext) { + ServiceIdentityProvider siaProvider) { if (url == null) { ztsUrl = lookupZTSUrl(); @@ -1197,7 +1199,16 @@ boolean prefetchToken(String domainName, String roleName, Integer minExpiryTime, } String getRoleTokenCacheKey(String domainName, String roleName, String proxyForPrincipal) { - return getRoleTokenCacheKey(domain, service, domainName, roleName, proxyForPrincipal); + + // if we don't have a tenant domain specified but we have a ssl context + // then we're going to use the hash code for our sslcontext as the + // value for our tenant + + String tenantDomain = domain; + if (domain == null && sslContext != null) { + tenantDomain = sslContext.toString(); + } + return getRoleTokenCacheKey(tenantDomain, service, domainName, roleName, proxyForPrincipal); } static String getRoleTokenCacheKey(String tenantDomain, String tenantService, String domainName,