Skip to content

Commit

Permalink
Implement Copper Argos based ZTS provider (#613)
Browse files Browse the repository at this point in the history
* Implement Copper Argos based ZTS provider

* For ZTSProvider validate public key in CSR matches public key in Athenz
  • Loading branch information
havetisyan committed Jan 2, 2019
1 parent 88475d3 commit ad75782
Show file tree
Hide file tree
Showing 31 changed files with 1,432 additions and 459 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,6 @@ rdl/rdl-gen-athenz-server/rdl-gen-athenz-server
aws-setup/ui-setup/tars/
aws-setup/zms-setup/tars/
aws-setup/zts-setup/tars/
aws-setup/athenz-conf-aws/pkg/
libs/go/athenzutils/pkg/
**test-output**
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@

public interface InstanceProvider {

enum Scheme {
HTTP,
CLASS,
UNKNOWN
}

/**
* Get Provider scheme. Currently supported schemes are HTTP
* or CLASS. By default we'll return UNKNOWN.
*/

default Scheme getProviderScheme() {
return Scheme.UNKNOWN;
}

/**
* Set provider details and initialize the provider object
* @param provider name of the provider (service identity name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,10 @@ public InstanceConfirmation postInstanceConfirmation(InstanceConfirmation confir
throw new ResourceException(ResourceException.FORBIDDEN, "Register Confirmation Exception");
}
int code = response.getStatus();
switch (code) {
case ResourceException.OK:
return response.readEntity(InstanceConfirmation.class);
default:
throw new ResourceException(code, responseText(response));
if (code == ResourceException.OK) {
return response.readEntity(InstanceConfirmation.class);
}
throw new ResourceException(code, responseText(response));
}

public InstanceConfirmation postRefreshConfirmation(InstanceConfirmation confirmation) {
Expand All @@ -120,11 +118,9 @@ public InstanceConfirmation postRefreshConfirmation(InstanceConfirmation confirm
throw new ResourceException(ResourceException.FORBIDDEN, "Refresh Confirmation Exception");
}
int code = response.getStatus();
switch (code) {
case ResourceException.OK:
return response.readEntity(InstanceConfirmation.class);
default:
throw new ResourceException(code, responseText(response));
if (code == ResourceException.OK) {
return response.readEntity(InstanceConfirmation.class);
}
throw new ResourceException(code, responseText(response));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ void setConfirmationAttributes(InstanceConfirmation confirmation, boolean sshCer
// and we always do not allow ssh certs

Map<String, String> attributes = new HashMap<>();
attributes.put(ZTS_CERT_USAGE, ZTS_CERT_USAGE_CLIENT);
attributes.put(ZTS_CERT_SSH, "false");
attributes.put(InstanceUtils.ZTS_CERT_USAGE, InstanceUtils.ZTS_CERT_USAGE_CLIENT);
attributes.put(InstanceUtils.ZTS_CERT_SSH, "false");
confirmation.setAttributes(attributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,9 @@ public class InstanceAWSProvider implements InstanceProvider {
static final String ATTR_ACCOUNT_ID = "accountId";
static final String ATTR_REGION = "region";
static final String ATTR_PENDING_TIME = "pendingTime";

static final String ATTR_INSTANCE_ID = "instanceId";

static final String ZTS_CERT_USAGE = "certUsage";
static final String ZTS_CERT_EXPIRY_TIME = "certExpiryTime";
static final String ZTS_CERT_SSH = "certSSH";
static final String ZTS_CERT_USAGE_CLIENT = "client";

static final String ZTS_CERT_INSTANCE_ID = ".instanceid.athenz.";
static final String ZTS_INSTANCE_SAN_DNS = "sanDNS";
static final String ZTS_INSTANCE_AWS_ACCOUNT = "cloudAccount";
static final String ZTS_INSTANCE_ID = "instanceId";

static final String AWS_PROP_PUBLIC_CERT = "athenz.zts.aws_public_cert";
static final String AWS_PROP_BOOT_TIME_OFFSET = "athenz.zts.aws_boot_time_offset";
Expand All @@ -78,6 +69,11 @@ public class InstanceAWSProvider implements InstanceProvider {
String awsRegion;
String dnsSuffix = null;

@Override
public Scheme getProviderScheme() {
return Scheme.HTTP;
}

@Override
public void initialize(String provider, String providerEndpoint, SSLContext sslContext,
KeyStore keyStore) {
Expand Down Expand Up @@ -128,22 +124,6 @@ public ResourceException error(int errorCode, String message) {
return new ResourceException(errorCode, message);
}

String getInstanceProperty(final Map<String, String> attributes, final String propertyName) {

if (attributes == null) {
LOGGER.error("getInstanceProperty: no attributes available");
return null;
}

final String value = attributes.get(propertyName);
if (value == null) {
LOGGER.error("getInstanceProperty: " + propertyName + " attribute not available");
return null;
}

return value;
}

boolean validateAWSAccount(final String awsAccount, final String docAccount, StringBuilder errMsg) {

if (!awsAccount.equalsIgnoreCase(docAccount)) {
Expand Down Expand Up @@ -243,7 +223,7 @@ boolean validateAWSDocument(final String provider, AWSAttestationData info,

// verify that the boot up time for the instance is now

return checkTime ? validateInstanceBootTime(instanceDocument, errMsg) : true;
return !checkTime || validateInstanceBootTime(instanceDocument, errMsg);
}

String getInstanceId(AWSAttestationData info, Struct instanceDocument) {
Expand All @@ -268,82 +248,6 @@ private boolean validateInstanceBootTime(Struct instanceDocument, StringBuilder
return true;
}

boolean validateCertRequestHostnames(final Map<String, String> attributes, final String domain,
final String service, StringBuilder instanceId) {

// make sure we have valid dns suffix specified

if (dnsSuffix == null || dnsSuffix.isEmpty()) {
LOGGER.error("No AWS DNS suffix specified for validation");
return false;
}

// first check to see if we're given any hostnames to validate
// if the list is empty then something is not right thus we'll
// reject the request

final String hostnames = getInstanceProperty(attributes, ZTS_INSTANCE_SAN_DNS);
if (hostnames == null || hostnames.isEmpty()) {
LOGGER.error("Request contains no SAN DNS entries for validation");
return false;
}

// generate the expected hostname for check

final String hostNameCheck = service + "." + domain.replace('.', '-') + "." + dnsSuffix;

// validate the entries

boolean hostCheck = false;
boolean instanceIdCheck = false;

String[] hosts = hostnames.split(",");

// we only allow two hostnames in our AWS CSR:
// service.<domain-with-dashes>.<dns-suffix>
// <instance-id>.instanceid.athenz.<dns-suffix>

if (hosts.length != 2) {
LOGGER.error("Request does not contain expected number of SAN DNS entries: {}",
hosts.length);
return false;
}

for (String host : hosts) {

int idx = host.indexOf(ZTS_CERT_INSTANCE_ID);
if (idx != -1) {
instanceId.append(host, 0, idx);
if (!dnsSuffix.equals(host.substring(idx + ZTS_CERT_INSTANCE_ID.length()))) {
LOGGER.error("Host: {} does not have expected instance id format", host);
return false;
}

instanceIdCheck = true;
} else {
if (!hostNameCheck.equals(host)) {
LOGGER.error("Unable to verify SAN DNS entry: {}", host);
return false;
}
hostCheck = true;
}
}

// report error cases separately for easier debugging

if (!instanceIdCheck) {
LOGGER.error("Request does not contain expected instance id SAN DNS entry");
return false;
}

if (!hostCheck) {
LOGGER.error("Request does not contain expected host SAN DNS entry");
return false;
}

return true;
}

@Override
public InstanceConfirmation confirmInstance(InstanceConfirmation confirmation) {

Expand All @@ -357,7 +261,7 @@ public InstanceConfirmation confirmInstance(InstanceConfirmation confirmation) {
// before doing anything else we want to make sure our
// object has an associated aws account id

final String awsAccount = getInstanceProperty(instanceAttributes, ZTS_INSTANCE_AWS_ACCOUNT);
final String awsAccount = InstanceUtils.getInstanceProperty(instanceAttributes, ZTS_INSTANCE_AWS_ACCOUNT);
if (awsAccount == null) {
throw error("Unable to extract AWS Account id");
}
Expand All @@ -373,8 +277,8 @@ public InstanceConfirmation confirmInstance(InstanceConfirmation confirmation) {
// validate the certificate host names

StringBuilder instanceId = new StringBuilder(256);
if (!validateCertRequestHostnames(instanceAttributes, instanceDomain,
instanceService, instanceId)) {
if (!InstanceUtils.validateCertRequestHostnames(instanceAttributes, instanceDomain,
instanceService, dnsSuffix, instanceId)) {
throw error("Unable to validate certificate request hostnames");
}

Expand Down Expand Up @@ -430,14 +334,15 @@ public InstanceConfirmation refreshInstance(InstanceConfirmation confirmation) {
// before doing anything else we want to make sure our
// object has an associated aws account id

final String awsAccount = getInstanceProperty(instanceAttributes, ZTS_INSTANCE_AWS_ACCOUNT);
final String awsAccount = InstanceUtils.getInstanceProperty(instanceAttributes, ZTS_INSTANCE_AWS_ACCOUNT);
if (awsAccount == null) {
throw error("Unable to extract AWS Account id");
}

// extract the instance id as well

final String instanceId = getInstanceProperty(instanceAttributes, ZTS_INSTANCE_ID);
final String instanceId = InstanceUtils.getInstanceProperty(instanceAttributes,
InstanceUtils.ZTS_INSTANCE_ID);
if (instanceId == null) {
throw error("Unable to extract Instance Id");
}
Expand Down Expand Up @@ -482,9 +387,9 @@ public InstanceConfirmation refreshInstance(InstanceConfirmation confirmation) {
void setConfirmationAttributes(InstanceConfirmation confirmation, boolean instanceDocumentCreds) {

Map<String, String> attributes = new HashMap<>();
attributes.put(ZTS_CERT_SSH, Boolean.toString(instanceDocumentCreds));
attributes.put(InstanceUtils.ZTS_CERT_SSH, Boolean.toString(instanceDocumentCreds));
if (!instanceDocumentCreds) {
attributes.put(ZTS_CERT_EXPIRY_TIME, Long.toString(certValidityTime));
attributes.put(InstanceUtils.ZTS_CERT_EXPIRY_TIME, Long.toString(certValidityTime));
}
confirmation.setAttributes(attributes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class InstanceHttpProvider implements InstanceProvider {
private static final String PROP_READ_TIMEOUT = "athenz.instance.provider.client.read_timeout";
private static final String PROP_CONNECT_TIMEOUT = "athenz.instance.provider.client.connect_timeout";

@Override
public Scheme getProviderScheme() {
return Scheme.HTTP;
}

@Override
public void initialize(String provider, String providerEndpoint, SSLContext sslContext,
KeyStore keyStore) {
Expand Down

0 comments on commit ad75782

Please sign in to comment.