Skip to content

Commit

Permalink
support athenz as oidc provider for aws iam (#2190)
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Avetisyan <[email protected]>
Co-authored-by: Henry Avetisyan <[email protected]>
  • Loading branch information
havetisyan and havetisyan authored May 30, 2023
1 parent 43f4343 commit 168baa4
Show file tree
Hide file tree
Showing 20 changed files with 404 additions and 125 deletions.
4 changes: 2 additions & 2 deletions clients/go/zts/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1156,9 +1156,9 @@ func (client ZTSClient) PostAccessTokenRequest(request AccessTokenRequest) (*Acc
}
}

func (client ZTSClient) GetOIDCResponse(responseType string, clientId ServiceName, redirectUri string, scope string, state EntityName, nonce EntityName, keyType SimpleName, fullArn *bool, expiryTime *int32, output SimpleName) (*OIDCResponse, string, error) {
func (client ZTSClient) GetOIDCResponse(responseType string, clientId ServiceName, redirectUri string, scope string, state EntityName, nonce EntityName, keyType SimpleName, fullArn *bool, expiryTime *int32, output SimpleName, roleInAudClaim *bool) (*OIDCResponse, string, error) {
var data *OIDCResponse
url := client.URL + "/oauth2/auth" + encodeParams(encodeStringParam("response_type", string(responseType), ""), encodeStringParam("client_id", string(clientId), ""), encodeStringParam("redirect_uri", string(redirectUri), ""), encodeStringParam("scope", string(scope), ""), encodeStringParam("state", string(state), ""), encodeStringParam("nonce", string(nonce), ""), encodeStringParam("keyType", string(keyType), ""), encodeOptionalBoolParam("fullArn", fullArn), encodeOptionalInt32Param("expiryTime", expiryTime), encodeStringParam("output", string(output), ""))
url := client.URL + "/oauth2/auth" + encodeParams(encodeStringParam("response_type", string(responseType), ""), encodeStringParam("client_id", string(clientId), ""), encodeStringParam("redirect_uri", string(redirectUri), ""), encodeStringParam("scope", string(scope), ""), encodeStringParam("state", string(state), ""), encodeStringParam("nonce", string(nonce), ""), encodeStringParam("keyType", string(keyType), ""), encodeOptionalBoolParam("fullArn", fullArn), encodeOptionalInt32Param("expiryTime", expiryTime), encodeStringParam("output", string(output), ""), encodeOptionalBoolParam("roleInAudClaim", roleInAudClaim))
resp, err := client.httpGet(url, nil)
if err != nil {
return nil, "", err
Expand Down
1 change: 1 addition & 0 deletions clients/go/zts/zts_schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3168,7 +3168,7 @@ public OIDCResponse getIDToken(String responseType, String clientId, String redi
try {
Map<String, List<String>> responseHeaders = new HashMap<>();
oidcResponse = ztsClient.getOIDCResponse(responseType, clientId, redirectUri, scope,
state, Crypto.randomSalt(), keyType, fullArn, expiryTime, "json", responseHeaders);
state, Crypto.randomSalt(), keyType, fullArn, expiryTime, "json", false, responseHeaders);

} catch (ResourceException ex) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ public AccessTokenResponse postAccessTokenRequest(String request) throws URISynt
}
}

public OIDCResponse getOIDCResponse(String responseType, String clientId, String redirectUri, String scope, String state, String nonce, String keyType, Boolean fullArn, Integer expiryTime, String output, java.util.Map<String, java.util.List<String>> headers) throws URISyntaxException, IOException {
public OIDCResponse getOIDCResponse(String responseType, String clientId, String redirectUri, String scope, String state, String nonce, String keyType, Boolean fullArn, Integer expiryTime, String output, Boolean roleInAudClaim, java.util.Map<String, java.util.List<String>> headers) throws URISyntaxException, IOException {
UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/oauth2/auth");
URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri());
if (responseType != null) {
Expand Down Expand Up @@ -963,6 +963,9 @@ public OIDCResponse getOIDCResponse(String responseType, String clientId, String
if (output != null) {
uriBuilder.setParameter("output", output);
}
if (roleInAudClaim != null) {
uriBuilder.setParameter("roleInAudClaim", String.valueOf(roleInAudClaim));
}
HttpUriRequest httpUriRequest = RequestBuilder.get()
.setUri(uriBuilder.build())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2208,7 +2208,7 @@ public void testGetAWSTemporaryCredentialsException() {
@Test
public void testHostnameVerifierSupport() {

ZTSRDLGeneratedClientMock client = new ZTSRDLGeneratedClientMock("http://localhost:4080", (HostnameVerifier) null);
ZTSRDLGeneratedClientMock client = new ZTSRDLGeneratedClientMock("http://localhost:4080", null);
HostnameVerifier hostnameVerifier = client.getHostnameVerifier();
assertTrue(hostnameVerifier == null || hostnameVerifier instanceof org.apache.http.conn.ssl.DefaultHostnameVerifier);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ public AccessTokenResponse postAccessTokenRequest(String request) {
@Override
public OIDCResponse getOIDCResponse(String responseType, String clientId, String redirectUri, String scope,
String state, String nonce, String keyType, Boolean fullArn, Integer expiryTime,
String output, Map<String, List<String>> headers) throws URISyntaxException, IOException {
String output, Boolean roleInAudClaim, Map<String, List<String>> headers)
throws URISyntaxException, IOException {

// some exception test cases based on the state value
if (state != null) {
Expand Down
10 changes: 10 additions & 0 deletions containers/jetty/conf/athenz.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ athenz.port=0
# ports are specified, https will be selected for the protocol.
#athenz.status_port=

# Port for handling OIDC requests. If different than the configured
# https port, then the server will create a separate connector
# to handle the oidc requests only. This includes issuing id
# tokens, returning public keys and openid configuration details.
# All other requests on this port will be rejected. This is useful
# when you want to integrate with another component that requires
# the service to run on a specific port - e.g. AWS IAM OIDC provider
# requires it to run on port 443 only.
#athenz.oidc_port=

# Set the number of days before rotated access log files are deleted
#athenz.access_log_retain_days=31

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public final class AthenzConsts {

public static final String ATHENZ_PROP_HTTP_PORT = "athenz.port";
public static final String ATHENZ_PROP_HTTPS_PORT = "athenz.tls_port";
public static final String ATHENZ_PROP_OIDC_PORT = "athenz.oidc_port";
public static final String ATHENZ_PROP_STATUS_PORT = "athenz.status_port";

public static final int ATHENZ_HTTPS_PORT_DEFAULT = 4443;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,17 @@ void addHTTPSConnector(HttpConfiguration httpsConfig, int httpsPort, boolean pro
}
}
}


HttpConfiguration getHttpsConfig(HttpConfiguration httpConfig, int httpsPort, boolean sniRequired, boolean sniHostCheck) {
HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.setSecureScheme("https");
httpsConfig.setSecurePort(httpsPort);
httpsConfig.addCustomizer(new SecureRequestCustomizer(sniRequired, sniHostCheck, -1L, false));
return httpsConfig;
}

public void addHTTPConnectors(HttpConfiguration httpConfig, int httpPort, int httpsPort,
int statusPort) {
int oidcPort, int statusPort) {

int idleTimeout = Integer.parseInt(
System.getProperty(AthenzConsts.ATHENZ_PROP_IDLE_TIMEOUT, "30000"));
Expand All @@ -500,38 +508,34 @@ public void addHTTPConnectors(HttpConfiguration httpConfig, int httpPort, int ht
connectionLogger = jettyConnectionLoggerFactory.create();
}

boolean sniRequired = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_SNI_REQUIRED, "false"));
boolean sniHostCheck = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_SNI_HOSTCHECK, "true"));
boolean needClientAuth = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_CLIENT_AUTH, "false"));

// HTTPS Connector

if (httpsPort > 0) {
HttpConfiguration httpsConfig = getHttpsConfig(httpConfig, httpsPort, sniRequired, sniHostCheck);
addHTTPSConnector(httpsConfig, httpsPort, proxyProtocol, listenHost,
idleTimeout, needClientAuth, connectionLogger);
}

boolean sniRequired = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_SNI_REQUIRED, "false"));
boolean sniHostCheck = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_SNI_HOSTCHECK, "true"));

HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.setSecureScheme("https");
httpsConfig.setSecurePort(httpsPort);
httpsConfig.addCustomizer(new SecureRequestCustomizer(sniRequired, sniHostCheck, -1L, false));

boolean needClientAuth = Boolean.parseBoolean(
System.getProperty(AthenzConsts.ATHENZ_PROP_CLIENT_AUTH, "false"));
// OIDC Connector - only if it's different from HTTPS

addHTTPSConnector(httpsConfig, httpsPort, proxyProtocol, listenHost,
if (oidcPort > 0 && oidcPort != httpsPort) {
HttpConfiguration httpsConfig = getHttpsConfig(httpConfig, oidcPort, sniRequired, sniHostCheck);
addHTTPSConnector(httpsConfig, oidcPort, proxyProtocol, listenHost,
idleTimeout, needClientAuth, connectionLogger);
}

// Status Connector - only if it's different from HTTP/HTTPS

if (statusPort > 0 && statusPort != httpPort && statusPort != httpsPort) {

if (httpsPort > 0) {

HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.setSecureScheme("https");
httpsConfig.setSecurePort(httpsPort);
httpsConfig.addCustomizer(new SecureRequestCustomizer(false, false, -1L, false));

HttpConfiguration httpsConfig = getHttpsConfig(httpConfig, httpsPort, false, false);
addHTTPSConnector(httpsConfig, statusPort, false, listenHost, idleTimeout, false, connectionLogger);
} else if (httpPort > 0) {
addHTTPConnector(httpConfig, statusPort, false, listenHost, idleTimeout);
Expand Down Expand Up @@ -592,7 +596,11 @@ public static AthenzJettyContainer createJettyContainer() {
AthenzConsts.ATHENZ_HTTP_PORT_DEFAULT);
int httpsPort = ConfigProperties.getPortNumber(AthenzConsts.ATHENZ_PROP_HTTPS_PORT,
AthenzConsts.ATHENZ_HTTPS_PORT_DEFAULT);


// extract the port for oidc requests if one is configured

int oidcPort = ConfigProperties.getPortNumber(AthenzConsts.ATHENZ_PROP_OIDC_PORT, 0);

// for status port we'll use the protocol specified for the regular http
// port. if both http and https are provided then https will be picked
// it could also be either one of the values specified as well
Expand All @@ -604,14 +612,14 @@ public static AthenzJettyContainer createJettyContainer() {
AthenzJettyContainer container = new AthenzJettyContainer();
container.setBanner("http://" + serverHostName + " http port: " +
httpPort + " https port: " + httpsPort + " status port: " +
statusPort);
statusPort + " oidc port: " + oidcPort);

int maxThreads = Integer.parseInt(System.getProperty(AthenzConsts.ATHENZ_PROP_MAX_THREADS,
Integer.toString(AthenzConsts.ATHENZ_HTTP_MAX_THREADS)));
container.createServer(maxThreads);

HttpConfiguration httpConfig = container.newHttpConfiguration();
container.addHTTPConnectors(httpConfig, httpPort, httpsPort, statusPort);
container.addHTTPConnectors(httpConfig, httpPort, httpsPort, oidcPort, statusPort);
container.addServletHandlers(serverHostName);

container.addRequestLogHandler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,17 +215,20 @@ public void testHttpConnectorsBoth() {
container.createServer(100);

HttpConfiguration httpConfig = container.newHttpConfiguration();
container.addHTTPConnectors(httpConfig, 8081, 8082, 0);
container.addHTTPConnectors(httpConfig, 8081, 8082, 443, 0);

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
assertEquals(connectors.length, 2);
assertEquals(connectors.length, 3);

assertEquals(connectors[0].getIdleTimeout(), 10001);
assertTrue(connectors[0].getProtocols().contains("http/1.1"));

assertTrue(connectors[1].getProtocols().contains("http/1.1"));
assertTrue(connectors[1].getProtocols().contains("ssl"));

assertTrue(connectors[2].getProtocols().contains("http/1.1"));
assertTrue(connectors[2].getProtocols().contains("ssl"));
}

@Test
Expand All @@ -247,7 +250,7 @@ public void testNonExistantKeyStore() {
HttpConfiguration httpConfig = container.newHttpConfiguration();
try {
// This should throw
container.addHTTPConnectors(httpConfig, 8081, 8082, 0);
container.addHTTPConnectors(httpConfig, 8081, 8082, 0, 0);
fail();
} catch (IllegalArgumentException exception) {
// as expected
Expand All @@ -270,7 +273,7 @@ public void testHttpConnectorsHttpsOnly() {
container.createServer(100);

HttpConfiguration httpConfig = container.newHttpConfiguration();
container.addHTTPConnectors(httpConfig, 0, 8082, 0);
container.addHTTPConnectors(httpConfig, 0, 8082, 0, 0);

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
Expand All @@ -296,7 +299,7 @@ public void testHttpConnectorsHttpOnly() {
container.createServer(100);

HttpConfiguration httpConfig = container.newHttpConfiguration();
container.addHTTPConnectors(httpConfig, 8081, 0, 0);
container.addHTTPConnectors(httpConfig, 8081, 0, 0, 0);

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
Expand Down Expand Up @@ -447,7 +450,7 @@ public void testInitContainerValidPorts() {

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
assertEquals(connectors.length, 2);
assertEquals(connectors.length, 3);

assertTrue(connectors[0].getProtocols().contains("http/1.1"));

Expand All @@ -460,24 +463,29 @@ public void testInitContainerOnlyHTTPSPort() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "0");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "4443");
System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "8443");
System.setProperty("yahoo.zms.debug.user_authority", "true");

AthenzJettyContainer container = AthenzJettyContainer.createJettyContainer();
assertNotNull(container);

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
assertEquals(connectors.length, 1);
assertEquals(connectors.length, 2);

assertTrue(connectors[0].getProtocols().contains("http/1.1"));
assertTrue(connectors[0].getProtocols().contains("ssl"));

assertTrue(connectors[1].getProtocols().contains("http/1.1"));
assertTrue(connectors[1].getProtocols().contains("ssl"));
}

@Test
public void testInitContainerOnlyHTTPPort() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "4080");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "0");
System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "0");

AthenzJettyContainer container = AthenzJettyContainer.createJettyContainer();
assertNotNull(container);
Expand All @@ -495,25 +503,30 @@ public void testInitContainerInvalidHTTPPort() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "-10");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "4443");

System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "443");

AthenzJettyContainer container = AthenzJettyContainer.createJettyContainer();
assertNotNull(container);

Server server = container.getServer();
Connector[] connectors = server.getConnectors();
assertEquals(connectors.length, 2);
assertEquals(connectors.length, 3);

assertTrue(connectors[0].getProtocols().contains("http/1.1"));

assertTrue(connectors[1].getProtocols().contains("http/1.1"));
assertTrue(connectors[1].getProtocols().contains("ssl"));

assertTrue(connectors[2].getProtocols().contains("http/1.1"));
assertTrue(connectors[2].getProtocols().contains("ssl"));
}

@Test
public void testInitContainerInvalidHTTPSPort() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "4080");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "-10");
System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "0");

AthenzJettyContainer container = AthenzJettyContainer.createJettyContainer();
assertNotNull(container);
Expand All @@ -533,6 +546,7 @@ public void testInitContainerOptionalFeatures() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "4080");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "4443");
System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "4443");
System.setProperty(AthenzConsts.ATHENZ_PROP_DEBUG, "true");
System.setProperty(AthenzConsts.ATHENZ_PROP_GZIP_SUPPORT, "true");
System.setProperty(AthenzConsts.ATHENZ_PROP_HEALTH_CHECK_URI_LIST, "/status.html");
Expand All @@ -559,6 +573,7 @@ public void testInitContainerStatusPortHTTPS() {

System.setProperty(AthenzConsts.ATHENZ_PROP_HTTP_PORT, "4080");
System.setProperty(AthenzConsts.ATHENZ_PROP_HTTPS_PORT, "4443");
System.setProperty(AthenzConsts.ATHENZ_PROP_OIDC_PORT, "8443");
System.setProperty(AthenzConsts.ATHENZ_PROP_STATUS_PORT, "4444");

AthenzJettyContainer container = AthenzJettyContainer.createJettyContainer();
Expand Down
1 change: 1 addition & 0 deletions core/zts/src/main/java/com/yahoo/athenz/zts/ZTSSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ private static Schema build() {
.queryParam("fullArn", "fullArn", "Bool", false, "flag to indicate to use full arn in group claim (e.g. sports:role.deployer instead of deployer)")
.queryParam("expiryTime", "expiryTime", "Int32", null, "optional expiry period specified in seconds")
.queryParam("output", "output", "SimpleName", null, "optional output format of json")
.queryParam("roleInAudClaim", "roleInAudClaim", "Bool", false, "flag to indicate to include role name in the audience claim only if we have a single role in response")
.output("Location", "location", "String", "return location header with id token")
.auth("", "", true)
.expected("OK")
Expand Down
Loading

0 comments on commit 168baa4

Please sign in to comment.