Skip to content

Commit

Permalink
Update ZTS Client AWS support + fix caching with certs (#356)
Browse files Browse the repository at this point in the history
* #350 zts role token restriction on appid move to authz service

* Update ZTS Client AWS support + fix caching with certs
  • Loading branch information
havetisyan authored Dec 16, 2017
1 parent c63ce3d commit da9ee2c
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 48 deletions.
2 changes: 1 addition & 1 deletion clients/java/zts/examples/tls-support/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<athenz.version>1.7.33</athenz.version>
<athenz.version>1.7.35</athenz.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
25 changes: 18 additions & 7 deletions clients/java/zts/src/main/java/com/yahoo/athenz/zts/ZTSClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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
}

Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit da9ee2c

Please sign in to comment.