Skip to content

Commit

Permalink
Merge pull request #32614 from vespa-engine/freva/sd
Browse files Browse the repository at this point in the history
ZtsClient: Support getting Azure temporary credentials
  • Loading branch information
hakonhall authored Oct 18, 2024
2 parents 3426125 + c1b088b commit a58bb23
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,8 @@
/**
* @author mortent
*/
public class AwsTemporaryCredentials {
private final String accessKeyId;
private final String secretAccessKey;
private final String sessionToken;
private final Instant expiration;

public AwsTemporaryCredentials(String accessKeyId, String secretAccessKey, String sessionToken, Instant expiration) {
this.accessKeyId = accessKeyId;
this.secretAccessKey = secretAccessKey;
this.sessionToken = sessionToken;
this.expiration = expiration;
}

public String accessKeyId() {
return accessKeyId;
}

public String secretAccessKey() {
return secretAccessKey;
}

public String sessionToken() {
return sessionToken;
}

public Instant expiration() {
return expiration;
}
public record AwsTemporaryCredentials(String accessKeyId,
String secretAccessKey,
String sessionToken,
Instant expiration) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.api;

import java.time.Instant;

/**
* @author freva
*/
public record AzureTemporaryCredentials(String azureSubscription,
String azureTenant,
String accessToken,
Instant expiration) {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.client.zms;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.athenz.auth.util.Crypto;
import com.yahoo.security.KeyUtils;
import com.yahoo.vespa.athenz.api.AthenzAssertion;
Expand Down Expand Up @@ -52,8 +51,6 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.yahoo.yolean.Exceptions.uncheck;


/**
* @author bjorncs
Expand Down Expand Up @@ -231,10 +228,9 @@ public AthenzDomainMeta getDomainMeta(AthenzDomain domain) {

@Override
public void updateDomain(AthenzDomain domain, String mainKey, Map<String, Object> attributes) {
String domainMeta = uncheck(() -> new ObjectMapper().writeValueAsString(attributes));
HttpUriRequest request = RequestBuilder.put()
.setUri(zmsUrl.resolve("domain/%s/meta/system/%s".formatted(domain.getName(), mainKey)))
.setEntity(new StringEntity(domainMeta, ContentType.APPLICATION_JSON))
.setEntity(toJsonStringEntity(attributes))
.build();
execute(request, response -> readEntity(response, Void.class));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AwsRole;
import com.yahoo.vespa.athenz.api.AwsTemporaryCredentials;
import com.yahoo.vespa.athenz.api.AzureTemporaryCredentials;
import com.yahoo.vespa.athenz.api.NToken;
import com.yahoo.vespa.athenz.api.ZToken;
import com.yahoo.vespa.athenz.client.ErrorHandler;
import com.yahoo.vespa.athenz.client.common.ClientBase;
import com.yahoo.vespa.athenz.client.zms.bindings.AccessResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.AccessTokenResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.AwsTemporaryCredentialsResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.AzureTemporaryCredentialsResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.IdentityRefreshRequestEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.IdentityResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.InstanceIdentityCredentials;
Expand All @@ -24,6 +26,7 @@
import com.yahoo.vespa.athenz.client.zts.bindings.RoleCertificateRequestEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.RoleCertificateResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.RoleTokenResponseEntity;
import com.yahoo.vespa.athenz.client.zts.bindings.TemporaryCredentialsResponse;
import com.yahoo.vespa.athenz.client.zts.bindings.TenantDomainsResponseEntity;
import com.yahoo.vespa.athenz.client.zts.utils.IdentityCsrGenerator;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
Expand All @@ -39,6 +42,7 @@
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -228,6 +232,29 @@ public AwsTemporaryCredentials getAwsTemporaryCredentials(AthenzDomain athenzDom
});
}

@Override
public AzureTemporaryCredentials getAzureTemporaryCredentials(AthenzRole athenzRole, String azureIdentityId) {
return getExternalTemporaryCredentials("azure", athenzRole.domain(),
Map.of("athenzRoleName", athenzRole.roleName(), "azureClientId", azureIdentityId),
AzureTemporaryCredentialsResponseEntity.class);
}

@Override
public AzureTemporaryCredentials getAzureTemporaryCredentials(AthenzRole athenzRole, String azureResourceGroup, String azureIdentityName) {
return getExternalTemporaryCredentials("azure", athenzRole.domain(),
Map.of("athenzRoleName", athenzRole.roleName(), "azureResourceGroup", azureResourceGroup, "azureClientName", azureIdentityName),
AzureTemporaryCredentialsResponseEntity.class);
}

private <T, U extends TemporaryCredentialsResponse<T>> T getExternalTemporaryCredentials(String provider, AthenzDomain domain, Map<String, String> attributes, Class<U> responseClass) {
URI uri = ztsUrl.resolve("external/%s/domain/%s/creds".formatted(provider, domain.getName()));
RequestBuilder requestBuilder = RequestBuilder.post(uri)
.setEntity(toJsonStringEntity(Map.of("clientId", "%s.%s".formatted(domain.getName(), provider), "attributes", attributes)));

HttpUriRequest request = requestBuilder.build();
return execute(request, response -> readEntity(response, responseClass)).credentials();
}

@Override
public boolean hasAccess(AthenzResourceName resource, String action, AthenzIdentity identity) {
URI uri = ztsUrl.resolve(String.format("access/%s/%s?principal=%s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AwsRole;
import com.yahoo.vespa.athenz.api.AwsTemporaryCredentials;
import com.yahoo.vespa.athenz.api.AzureTemporaryCredentials;
import com.yahoo.vespa.athenz.api.ZToken;

import java.security.KeyPair;
Expand Down Expand Up @@ -167,7 +168,7 @@ default AthenzAccessToken getAccessToken(AthenzDomain domain) {
List<AthenzDomain> getTenantDomains(AthenzIdentity providerIdentity, AthenzIdentity userIdentity, String roleName);

/**
* Get aws temporary credentials
* Get AWS temporary credentials
*
* @param awsRole AWS role to get credentials for
* @param externalId External Id to get credentials, or <code>null</code> if not required
Expand All @@ -178,7 +179,7 @@ default AwsTemporaryCredentials getAwsTemporaryCredentials(AthenzDomain athenzDo
}

/**
* Get aws temporary credentials
* Get AWS temporary credentials
*
* @param awsRole AWS role to get credentials for
* @param duration Duration for which the credentials should be valid, or <code>null</code> to use default
Expand All @@ -187,6 +188,21 @@ default AwsTemporaryCredentials getAwsTemporaryCredentials(AthenzDomain athenzDo
*/
AwsTemporaryCredentials getAwsTemporaryCredentials(AthenzDomain athenzDomain, AwsRole awsRole, Duration duration, String externalId);

/**
* @param athenzRole Athenz role to use when assuming credentials
* @param azureIdentityId Client ID of the Azure identity to assume
* @return Azure temporary credentials
*/
AzureTemporaryCredentials getAzureTemporaryCredentials(AthenzRole athenzRole, String azureIdentityId);

/**
* @param athenzRole Athenz role to use when assuming credentials
* @param azureResourceGroup Azure resource group that contains the target identity
* @param azureIdentityName Name of the Azure Identity to assume
* @return Azure temporary credentials
*/
AzureTemporaryCredentials getAzureTemporaryCredentials(AthenzRole athenzRole, String azureResourceGroup, String azureIdentityName);

/**
* Check access to resource for a given principal
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
* @author mortent
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class AwsTemporaryCredentialsResponseEntity {
private AwsTemporaryCredentials credentials;
public class AwsTemporaryCredentialsResponseEntity implements TemporaryCredentialsResponse<AwsTemporaryCredentials> {
private final AwsTemporaryCredentials credentials;

public AwsTemporaryCredentialsResponseEntity(
@JsonProperty("accessKeyId") String accessKeyId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.client.zts.bindings;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.yahoo.vespa.athenz.api.AzureTemporaryCredentials;

import java.time.Instant;

/**
* @author freva
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class AzureTemporaryCredentialsResponseEntity implements TemporaryCredentialsResponse<AzureTemporaryCredentials> {
private final AzureTemporaryCredentials credentials;

public AzureTemporaryCredentialsResponseEntity(
@JsonProperty("attributes") Attributes attributes,
@JsonProperty("expiration") Instant expiration) {
this.credentials = new AzureTemporaryCredentials(
attributes.azureSubscription(),
attributes.azureTenant(),
attributes.accessToken(),
expiration);
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record Attributes(@JsonProperty("azureSubscription") String azureSubscription,
@JsonProperty("azureTenant") String azureTenant,
@JsonProperty("accessToken") String accessToken) { }

public AzureTemporaryCredentials credentials() {
return credentials;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.athenz.client.zts.bindings;

/**
* @author freva
*/
public interface TemporaryCredentialsResponse<T> {
T credentials();
}

0 comments on commit a58bb23

Please sign in to comment.