This change introduces support for using an http proxy for egress communication of the OpenID Connect realm.
This commit is contained in:
parent
7a67fb2d04
commit
af9f9d7f03
|
@ -1343,6 +1343,24 @@ id tokens with regards to their creation and expiration times.
|
||||||
Specifies whether to populate the {es} user's metadata with the values that are
|
Specifies whether to populate the {es} user's metadata with the values that are
|
||||||
provided by the OpenID Connect claims. Defaults to `true`.
|
provided by the OpenID Connect claims. Defaults to `true`.
|
||||||
|
|
||||||
|
`http.proxy.host`::
|
||||||
|
Specifies the address of the proxy server that will be used by the internal
|
||||||
|
http client for all back-channel communication to the OpenID Connect Provider
|
||||||
|
endpoints. This includes requests to the Token Endpoint, the Userinfo Endpoint
|
||||||
|
and requests to fetch the JSON Web Key Set from the OP if `op.jwkset_path` is
|
||||||
|
set as a URL.
|
||||||
|
|
||||||
|
`http.proxy.scheme`::
|
||||||
|
Specifies the protocol to use to connect to the proxy server that will be
|
||||||
|
used by the http client for all back-channel communication to the OpenID
|
||||||
|
Connect Provider endpoints. Defaults to `http`. Allowed values are
|
||||||
|
`http` or `https`.
|
||||||
|
|
||||||
|
`http.proxy.port`::
|
||||||
|
Specifies the port of the proxy server that will be used by the http
|
||||||
|
client for all backchannel communication to the OpenID Connect Provider
|
||||||
|
endpoints. Defaults to `80`.
|
||||||
|
|
||||||
`http.connect_timeout`::
|
`http.connect_timeout`::
|
||||||
Controls the behavior of the http client used for back-channel communication to
|
Controls the behavior of the http client used for back-channel communication to
|
||||||
the OpenID Connect Provider endpoints. Specifies the timeout until a connection
|
the OpenID Connect Provider endpoints. Specifies the timeout until a connection
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.core.security.authc.oidc;
|
package org.elasticsearch.xpack.core.security.authc.oidc;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
@ -19,7 +20,9 @@ import java.net.URISyntaxException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@ -139,6 +142,48 @@ public class OpenIdConnectRealmSettings {
|
||||||
public static final Setting.AffixSetting<Integer> HTTP_MAX_ENDPOINT_CONNECTIONS
|
public static final Setting.AffixSetting<Integer> HTTP_MAX_ENDPOINT_CONNECTIONS
|
||||||
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.max_endpoint_connections",
|
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.max_endpoint_connections",
|
||||||
key -> Setting.intSetting(key, 200, Setting.Property.NodeScope));
|
key -> Setting.intSetting(key, 200, Setting.Property.NodeScope));
|
||||||
|
public static final Setting.AffixSetting<String> HTTP_PROXY_HOST
|
||||||
|
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.proxy.host",
|
||||||
|
key -> Setting.simpleString(key, new Setting.Validator<String>() {
|
||||||
|
@Override
|
||||||
|
public void validate(String value) {
|
||||||
|
// There is no point in validating the hostname in itself without the scheme and port
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(String value, Map<Setting<?>, Object> settings) {
|
||||||
|
final String namespace = HTTP_PROXY_HOST.getNamespace(HTTP_PROXY_HOST.getConcreteSetting(key));
|
||||||
|
final Setting<Integer> portSetting = HTTP_PROXY_PORT.getConcreteSettingForNamespace(namespace);
|
||||||
|
final Integer port = (Integer) settings.get(portSetting);
|
||||||
|
final Setting<String> schemeSetting = HTTP_PROXY_SCHEME.getConcreteSettingForNamespace(namespace);
|
||||||
|
final String scheme = (String) settings.get(schemeSetting);
|
||||||
|
try {
|
||||||
|
new HttpHost(value, port, scheme);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("HTTP host for hostname [" + value + "] (from [" + key + "])," +
|
||||||
|
" port [" + port + "] (from [" + portSetting.getKey() + "]) and " +
|
||||||
|
"scheme [" + scheme + "] (from ([" + schemeSetting.getKey() + "]) is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Setting<?>> settings() {
|
||||||
|
final String namespace = HTTP_PROXY_HOST.getNamespace(HTTP_PROXY_HOST.getConcreteSetting(key));
|
||||||
|
final List<Setting<?>> settings = Arrays.asList(HTTP_PROXY_PORT.getConcreteSettingForNamespace(namespace),
|
||||||
|
HTTP_PROXY_SCHEME.getConcreteSettingForNamespace(namespace));
|
||||||
|
return settings.iterator();
|
||||||
|
}
|
||||||
|
}, Setting.Property.NodeScope));
|
||||||
|
public static final Setting.AffixSetting<Integer> HTTP_PROXY_PORT
|
||||||
|
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.proxy.port",
|
||||||
|
key -> Setting.intSetting(key, 80, 1, 65535, Setting.Property.NodeScope), () -> HTTP_PROXY_HOST);
|
||||||
|
public static final Setting.AffixSetting<String> HTTP_PROXY_SCHEME
|
||||||
|
= Setting.affixKeySetting(RealmSettings.realmSettingPrefix(TYPE), "http.proxy.scheme",
|
||||||
|
key -> Setting.simpleString(key, "http", value -> {
|
||||||
|
if (value.equals("http") == false && value.equals("https") == false) {
|
||||||
|
throw new IllegalArgumentException("Invalid value [" + value + "] for [" + key + "]. Only `http` or `https` are allowed.");
|
||||||
|
}
|
||||||
|
}, Setting.Property.NodeScope));
|
||||||
|
|
||||||
public static final ClaimSetting PRINCIPAL_CLAIM = new ClaimSetting("principal");
|
public static final ClaimSetting PRINCIPAL_CLAIM = new ClaimSetting("principal");
|
||||||
public static final ClaimSetting GROUPS_CLAIM = new ClaimSetting("groups");
|
public static final ClaimSetting GROUPS_CLAIM = new ClaimSetting("groups");
|
||||||
|
@ -151,7 +196,8 @@ public class OpenIdConnectRealmSettings {
|
||||||
RP_CLIENT_ID, RP_REDIRECT_URI, RP_RESPONSE_TYPE, RP_REQUESTED_SCOPES, RP_CLIENT_SECRET, RP_SIGNATURE_ALGORITHM,
|
RP_CLIENT_ID, RP_REDIRECT_URI, RP_RESPONSE_TYPE, RP_REQUESTED_SCOPES, RP_CLIENT_SECRET, RP_SIGNATURE_ALGORITHM,
|
||||||
RP_POST_LOGOUT_REDIRECT_URI, OP_AUTHORIZATION_ENDPOINT, OP_TOKEN_ENDPOINT, OP_USERINFO_ENDPOINT,
|
RP_POST_LOGOUT_REDIRECT_URI, OP_AUTHORIZATION_ENDPOINT, OP_TOKEN_ENDPOINT, OP_USERINFO_ENDPOINT,
|
||||||
OP_ENDSESSION_ENDPOINT, OP_ISSUER, OP_JWKSET_PATH, POPULATE_USER_METADATA, HTTP_CONNECT_TIMEOUT, HTTP_CONNECTION_READ_TIMEOUT,
|
OP_ENDSESSION_ENDPOINT, OP_ISSUER, OP_JWKSET_PATH, POPULATE_USER_METADATA, HTTP_CONNECT_TIMEOUT, HTTP_CONNECTION_READ_TIMEOUT,
|
||||||
HTTP_SOCKET_TIMEOUT, HTTP_MAX_CONNECTIONS, HTTP_MAX_ENDPOINT_CONNECTIONS, ALLOWED_CLOCK_SKEW);
|
HTTP_SOCKET_TIMEOUT, HTTP_MAX_CONNECTIONS, HTTP_MAX_ENDPOINT_CONNECTIONS, HTTP_PROXY_HOST, HTTP_PROXY_PORT,
|
||||||
|
HTTP_PROXY_SCHEME, ALLOWED_CLOCK_SKEW);
|
||||||
set.addAll(DelegatedAuthorizationSettings.getSettings(TYPE));
|
set.addAll(DelegatedAuthorizationSettings.getSettings(TYPE));
|
||||||
set.addAll(RealmSettings.getStandardSettings(TYPE));
|
set.addAll(RealmSettings.getStandardSettings(TYPE));
|
||||||
set.addAll(SSLConfigurationSettings.getRealmSettings(TYPE));
|
set.addAll(SSLConfigurationSettings.getRealmSettings(TYPE));
|
||||||
|
|
|
@ -42,6 +42,7 @@ import net.minidev.json.JSONObject;
|
||||||
import org.apache.commons.codec.Charsets;
|
import org.apache.commons.codec.Charsets;
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.auth.AuthenticationException;
|
import org.apache.http.auth.AuthenticationException;
|
||||||
|
@ -56,6 +57,7 @@ import org.apache.http.config.RegistryBuilder;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.impl.auth.BasicScheme;
|
import org.apache.http.impl.auth.BasicScheme;
|
||||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||||
|
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
|
||||||
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
||||||
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
|
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
|
||||||
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
||||||
|
@ -112,6 +114,9 @@ import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectReal
|
||||||
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_CONNECT_TIMEOUT;
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_CONNECT_TIMEOUT;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_MAX_CONNECTIONS;
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_MAX_CONNECTIONS;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_MAX_ENDPOINT_CONNECTIONS;
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_MAX_ENDPOINT_CONNECTIONS;
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_PROXY_HOST;
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_PROXY_PORT;
|
||||||
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_PROXY_SCHEME;
|
||||||
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_SOCKET_TIMEOUT;
|
import static org.elasticsearch.xpack.core.security.authc.oidc.OpenIdConnectRealmSettings.HTTP_SOCKET_TIMEOUT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -579,10 +584,14 @@ public class OpenIdConnectAuthenticator {
|
||||||
.setConnectTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_CONNECT_TIMEOUT).getMillis()))
|
.setConnectTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_CONNECT_TIMEOUT).getMillis()))
|
||||||
.setConnectionRequestTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_CONNECTION_READ_TIMEOUT).getSeconds()))
|
.setConnectionRequestTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_CONNECTION_READ_TIMEOUT).getSeconds()))
|
||||||
.setSocketTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_SOCKET_TIMEOUT).getMillis())).build();
|
.setSocketTimeout(Math.toIntExact(realmConfig.getSetting(HTTP_SOCKET_TIMEOUT).getMillis())).build();
|
||||||
CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
|
HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClients.custom()
|
||||||
.setConnectionManager(connectionManager)
|
.setConnectionManager(connectionManager)
|
||||||
.setDefaultRequestConfig(requestConfig)
|
.setDefaultRequestConfig(requestConfig);
|
||||||
.build();
|
if (realmConfig.hasSetting(HTTP_PROXY_HOST)) {
|
||||||
|
httpAsyncClientBuilder.setProxy(new HttpHost(realmConfig.getSetting(HTTP_PROXY_HOST),
|
||||||
|
realmConfig.getSetting(HTTP_PROXY_PORT), realmConfig.getSetting(HTTP_PROXY_SCHEME)));
|
||||||
|
}
|
||||||
|
CloseableHttpAsyncClient httpAsyncClient = httpAsyncClientBuilder.build();
|
||||||
httpAsyncClient.start();
|
httpAsyncClient.start();
|
||||||
return httpAsyncClient;
|
return httpAsyncClient;
|
||||||
});
|
});
|
||||||
|
|
|
@ -262,9 +262,9 @@ public class OpenIdConnectRealm extends Realm implements Releasable {
|
||||||
}
|
}
|
||||||
final ResponseType responseType;
|
final ResponseType responseType;
|
||||||
try {
|
try {
|
||||||
// This should never happen as it's already validated in the settings
|
|
||||||
responseType = ResponseType.parse(require(config, RP_RESPONSE_TYPE));
|
responseType = ResponseType.parse(require(config, RP_RESPONSE_TYPE));
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
|
// This should never happen as it's already validated in the settings
|
||||||
throw new SettingsException("Invalid value for " + RP_RESPONSE_TYPE.getKey(), e);
|
throw new SettingsException("Invalid value for " + RP_RESPONSE_TYPE.getKey(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,6 +263,69 @@ public class OpenIdConnectRealmSettingsTests extends ESTestCase {
|
||||||
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_SECRET)));
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_SECRET)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInvalidProxySchemeThrowsError() {
|
||||||
|
final Settings.Builder settingsBuilder = Settings.builder()
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_TOKEN_ENDPOINT), "https://op.example.com/token")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_JWKSET_PATH), "https://op.example.com/jwks.json")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.PRINCIPAL_CLAIM.getClaim()), "sub")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_REDIRECT_URI), "https://rp.my.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_ID), "rp-my")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_RESPONSE_TYPE), "code")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_HOST), "proxyhostname.org")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_SCHEME), "invalid");
|
||||||
|
settingsBuilder.setSecureSettings(getSecureSettings());
|
||||||
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> {
|
||||||
|
buildConfig(settingsBuilder.build()).getSetting(OpenIdConnectRealmSettings.HTTP_PROXY_SCHEME);
|
||||||
|
});
|
||||||
|
assertThat(exception.getMessage(),
|
||||||
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_SCHEME)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidProxyPortThrowsError() {
|
||||||
|
final Settings.Builder settingsBuilder = Settings.builder()
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_TOKEN_ENDPOINT), "https://op.example.com/token")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_JWKSET_PATH), "https://op.example.com/jwks.json")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.PRINCIPAL_CLAIM.getClaim()), "sub")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_REDIRECT_URI), "https://rp.my.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_ID), "rp-my")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_RESPONSE_TYPE), "code")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_HOST), "proxyhostname.org")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_PORT), 123456);
|
||||||
|
settingsBuilder.setSecureSettings(getSecureSettings());
|
||||||
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> {
|
||||||
|
buildConfig(settingsBuilder.build()).getSetting(OpenIdConnectRealmSettings.HTTP_PROXY_PORT);
|
||||||
|
});
|
||||||
|
assertThat(exception.getMessage(),
|
||||||
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_PORT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidProxyHostThrowsError() {
|
||||||
|
final Settings.Builder settingsBuilder = Settings.builder()
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_TOKEN_ENDPOINT), "https://op.example.com/token")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_JWKSET_PATH), "https://op.example.com/jwks.json")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.PRINCIPAL_CLAIM.getClaim()), "sub")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_REDIRECT_URI), "https://rp.my.com")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_ID), "rp-my")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_RESPONSE_TYPE), "code")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_HOST), "proxy hostname.org")
|
||||||
|
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_PORT), 8080);
|
||||||
|
settingsBuilder.setSecureSettings(getSecureSettings());
|
||||||
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> {
|
||||||
|
buildConfig(settingsBuilder.build()).getSetting(OpenIdConnectRealmSettings.HTTP_PROXY_HOST);
|
||||||
|
});
|
||||||
|
assertThat(exception.getMessage(), Matchers.allOf(
|
||||||
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_HOST)),
|
||||||
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_PORT)),
|
||||||
|
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.HTTP_PROXY_SCHEME))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
private MockSecureSettings getSecureSettings() {
|
private MockSecureSettings getSecureSettings() {
|
||||||
MockSecureSettings secureSettings = new MockSecureSettings();
|
MockSecureSettings secureSettings = new MockSecureSettings();
|
||||||
secureSettings.setString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_SECRET),
|
secureSettings.setString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_SECRET),
|
||||||
|
|
|
@ -12,13 +12,15 @@ dependencies {
|
||||||
}
|
}
|
||||||
testFixtures.useFixture ":x-pack:test:idp-fixture", "oidc-provider"
|
testFixtures.useFixture ":x-pack:test:idp-fixture", "oidc-provider"
|
||||||
|
|
||||||
String ephemeralPort;
|
String ephemeralOpPort
|
||||||
|
String ephemeralProxyPort
|
||||||
task setupPorts {
|
task setupPorts {
|
||||||
// Don't attempt to get ephemeral ports when Docker is not available
|
// Don't attempt to get ephemeral ports when Docker is not available
|
||||||
onlyIf { idpFixtureProject.postProcessFixture.state.skipped == false }
|
onlyIf { idpFixtureProject.postProcessFixture.state.skipped == false }
|
||||||
dependsOn idpFixtureProject.postProcessFixture
|
dependsOn idpFixtureProject.postProcessFixture
|
||||||
doLast {
|
doLast {
|
||||||
ephemeralPort = idpFixtureProject.postProcessFixture.ext."test.fixtures.oidc-provider.tcp.8080"
|
ephemeralOpPort = idpFixtureProject.postProcessFixture.ext."test.fixtures.oidc-provider.tcp.8080"
|
||||||
|
ephemeralProxyPort = idpFixtureProject.postProcessFixture.ext."test.fixtures.http-proxy.tcp.8888"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +39,9 @@ testClusters.integTest {
|
||||||
// OpenID Connect Realm 1 configured for authorization grant flow
|
// OpenID Connect Realm 1 configured for authorization grant flow
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.order', '2'
|
setting 'xpack.security.authc.realms.oidc.c2id.order', '2'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.op.issuer', 'http://localhost:8080'
|
setting 'xpack.security.authc.realms.oidc.c2id.op.issuer', 'http://localhost:8080'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.op.authorization_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id-login" }
|
setting 'xpack.security.authc.realms.oidc.c2id.op.authorization_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id-login" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.op.token_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id/token" }
|
setting 'xpack.security.authc.realms.oidc.c2id.op.token_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/token" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.op.userinfo_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id/userinfo" }
|
setting 'xpack.security.authc.realms.oidc.c2id.op.userinfo_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/userinfo" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.op.jwkset_path', 'op-jwks.json'
|
setting 'xpack.security.authc.realms.oidc.c2id.op.jwkset_path', 'op-jwks.json'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.rp.redirect_uri', 'https://my.fantastic.rp/cb'
|
setting 'xpack.security.authc.realms.oidc.c2id.rp.redirect_uri', 'https://my.fantastic.rp/cb'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id.rp.client_id', 'https://my.elasticsearch.org/rp'
|
setting 'xpack.security.authc.realms.oidc.c2id.rp.client_id', 'https://my.elasticsearch.org/rp'
|
||||||
|
@ -52,9 +54,9 @@ testClusters.integTest {
|
||||||
// OpenID Connect Realm 2 configured for implicit flow
|
// OpenID Connect Realm 2 configured for implicit flow
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.order', '3'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.order', '3'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.issuer', 'http://localhost:8080'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.issuer', 'http://localhost:8080'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.authorization_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id-login" }
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.authorization_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id-login" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.token_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id/token" }
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.token_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/token" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.userinfo_endpoint', { "http://127.0.0.1:${ephemeralPort}/c2id/userinfo" }
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.userinfo_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/userinfo" }
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.jwkset_path', 'op-jwks.json'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.op.jwkset_path', 'op-jwks.json'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.rp.redirect_uri', 'https://my.fantastic.rp/cb'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.rp.redirect_uri', 'https://my.fantastic.rp/cb'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.rp.client_id', 'elasticsearch-rp'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.rp.client_id', 'elasticsearch-rp'
|
||||||
|
@ -64,8 +66,24 @@ testClusters.integTest {
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.name', 'name'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.name', 'name'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.mail', 'email'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.mail', 'email'
|
||||||
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.groups', 'groups'
|
setting 'xpack.security.authc.realms.oidc.c2id-implicit.claims.groups', 'groups'
|
||||||
|
// OpenID Connect Realm 3 configured to use a proxy
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.order', '4'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.op.issuer', 'http://localhost:8080'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.op.authorization_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id-login" }
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.op.token_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/token" }
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.op.userinfo_endpoint', { "http://127.0.0.1:${ephemeralOpPort}/c2id/userinfo" }
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.op.jwkset_path', 'op-jwks.json'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.rp.redirect_uri', 'https://my.fantastic.rp/cb'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.rp.client_id', 'https://my.elasticsearch.org/rp'
|
||||||
|
keystore 'xpack.security.authc.realms.oidc.c2id-proxy.rp.client_secret', 'b07efb7a1cf6ec9462afe7b6d3ab55c6c7880262aa61ac28dded292aca47c9a2'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.rp.response_type', 'code'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.claims.principal', 'sub'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.claims.name', 'name'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.claims.mail', 'email'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.claims.groups', 'groups'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.http.proxy.host', '127.0.0.1'
|
||||||
|
setting 'xpack.security.authc.realms.oidc.c2id-proxy.http.proxy.port', {"${ephemeralProxyPort}"}
|
||||||
setting 'xpack.ml.enabled', 'false'
|
setting 'xpack.ml.enabled', 'false'
|
||||||
|
|
||||||
extraConfigFile 'op-jwks.json', idpFixtureProject.file("oidc/op-jwks.json")
|
extraConfigFile 'op-jwks.json', idpFixtureProject.file("oidc/op-jwks.json")
|
||||||
|
|
||||||
user username: "test_admin", password: "x-pack-test-password"
|
user username: "test_admin", password: "x-pack-test-password"
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class OpenIdConnectAuthIT extends ESRestTestCase {
|
||||||
|
|
||||||
private static final String REALM_NAME = "c2id";
|
private static final String REALM_NAME = "c2id";
|
||||||
private static final String REALM_NAME_IMPLICIT = "c2id-implicit";
|
private static final String REALM_NAME_IMPLICIT = "c2id-implicit";
|
||||||
|
private static final String REALM_NAME_PROXY = "c2id-proxy";
|
||||||
private static final String FACILITATOR_PASSWORD = "f@cilit@t0r";
|
private static final String FACILITATOR_PASSWORD = "f@cilit@t0r";
|
||||||
private static final String REGISTRATION_URL = "http://127.0.0.1:" + getEphemeralPortFromProperty("8080") + "/c2id/clients";
|
private static final String REGISTRATION_URL = "http://127.0.0.1:" + getEphemeralPortFromProperty("8080") + "/c2id/clients";
|
||||||
private static final String LOGIN_API = "http://127.0.0.1:" + getEphemeralPortFromProperty("8080") + "/c2id-login/api/";
|
private static final String LOGIN_API = "http://127.0.0.1:" + getEphemeralPortFromProperty("8080") + "/c2id-login/api/";
|
||||||
|
@ -244,22 +245,29 @@ public class OpenIdConnectAuthIT extends ESRestTestCase {
|
||||||
public void testAuthenticateWithCodeFlow() throws Exception {
|
public void testAuthenticateWithCodeFlow() throws Exception {
|
||||||
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME);
|
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME);
|
||||||
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
||||||
final String realm = randomBoolean() ? null : prepareAuthResponse.getRealm();
|
|
||||||
Tuple<String, String> tokens = completeAuthentication(redirectUri, prepareAuthResponse.getState(),
|
Tuple<String, String> tokens = completeAuthentication(redirectUri, prepareAuthResponse.getState(),
|
||||||
prepareAuthResponse.getNonce(), realm);
|
prepareAuthResponse.getNonce(), REALM_NAME);
|
||||||
verifyElasticsearchAccessTokenForCodeFlow(tokens.v1());
|
verifyElasticsearchAccessTokenForCodeFlow(tokens.v1());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAuthenticateWithImplicitFlow() throws Exception {
|
public void testAuthenticateWithImplicitFlow() throws Exception {
|
||||||
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME_IMPLICIT);
|
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME_IMPLICIT);
|
||||||
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
||||||
final String realm = randomBoolean() ? null : prepareAuthResponse.getRealm();
|
|
||||||
|
|
||||||
Tuple<String, String> tokens = completeAuthentication(redirectUri, prepareAuthResponse.getState(),
|
Tuple<String, String> tokens = completeAuthentication(redirectUri, prepareAuthResponse.getState(),
|
||||||
prepareAuthResponse.getNonce(), realm);
|
prepareAuthResponse.getNonce(), REALM_NAME_IMPLICIT);
|
||||||
verifyElasticsearchAccessTokenForImplicitFlow(tokens.v1());
|
verifyElasticsearchAccessTokenForImplicitFlow(tokens.v1());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateWithCodeFlowUsingHttpProxy() throws Exception {
|
||||||
|
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME_PROXY);
|
||||||
|
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
||||||
|
|
||||||
|
Tuple<String, String> tokens = completeAuthentication(redirectUri, prepareAuthResponse.getState(),
|
||||||
|
prepareAuthResponse.getNonce(), REALM_NAME_PROXY);
|
||||||
|
verifyElasticsearchAccessTokenForCodeFlow(tokens.v1());
|
||||||
|
}
|
||||||
|
|
||||||
public void testAuthenticateWithCodeFlowFailsForWrongRealm() throws Exception {
|
public void testAuthenticateWithCodeFlowFailsForWrongRealm() throws Exception {
|
||||||
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME);
|
final PrepareAuthResponse prepareAuthResponse = getRedirectedFromFacilitator(REALM_NAME);
|
||||||
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
final String redirectUri = authenticateAtOP(prepareAuthResponse.getAuthUri());
|
||||||
|
@ -377,7 +385,10 @@ public class OpenIdConnectAuthIT extends ESRestTestCase {
|
||||||
createRoleMappingRequest.setJsonEntity("{ \"roles\" : [\"kibana_admin\"]," +
|
createRoleMappingRequest.setJsonEntity("{ \"roles\" : [\"kibana_admin\"]," +
|
||||||
"\"enabled\": true," +
|
"\"enabled\": true," +
|
||||||
"\"rules\": {" +
|
"\"rules\": {" +
|
||||||
"\"field\": { \"realm.name\": \"" + REALM_NAME + "\"}" +
|
" \"any\" : [" +
|
||||||
|
" {\"field\": { \"realm.name\": \"" + REALM_NAME + "\"} }," +
|
||||||
|
" {\"field\": { \"realm.name\": \"" + REALM_NAME_PROXY + "\"} }" +
|
||||||
|
" ]" +
|
||||||
"}" +
|
"}" +
|
||||||
"}");
|
"}");
|
||||||
adminClient().performRequest(createRoleMappingRequest);
|
adminClient().performRequest(createRoleMappingRequest);
|
||||||
|
@ -409,13 +420,11 @@ public class OpenIdConnectAuthIT extends ESRestTestCase {
|
||||||
private URI authUri;
|
private URI authUri;
|
||||||
private String state;
|
private String state;
|
||||||
private String nonce;
|
private String nonce;
|
||||||
private String realm;
|
|
||||||
|
|
||||||
PrepareAuthResponse(URI authUri, String state, String nonce, @Nullable String realm) {
|
PrepareAuthResponse(URI authUri, String state, String nonce, @Nullable String realm) {
|
||||||
this.authUri = authUri;
|
this.authUri = authUri;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
this.realm = realm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
URI getAuthUri() {
|
URI getAuthUri() {
|
||||||
|
@ -430,6 +439,5 @@ public class OpenIdConnectAuthIT extends ESRestTestCase {
|
||||||
return nonce;
|
return nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getRealm() { return realm;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,16 @@ services:
|
||||||
|
|
||||||
oidc-provider:
|
oidc-provider:
|
||||||
image: "c2id/c2id-server:7.8"
|
image: "c2id/c2id-server:7.8"
|
||||||
|
depends_on:
|
||||||
|
- http-proxy
|
||||||
ports:
|
ports:
|
||||||
- "8080"
|
- "8080"
|
||||||
volumes:
|
volumes:
|
||||||
- ./oidc/override.properties:/etc/c2id/override.properties
|
- ./oidc/override.properties:/etc/c2id/override.properties
|
||||||
|
|
||||||
|
http-proxy:
|
||||||
|
image: "nginx:latest"
|
||||||
|
volumes:
|
||||||
|
- ./oidc/nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
ports:
|
||||||
|
- "8888"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
worker_processes 1;
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
# Acts as a simple forward http proxy for the OIDC realm configuration
|
||||||
|
listen 8888;
|
||||||
|
location / {
|
||||||
|
# oidc-provider is another container so we need to rewrite `127.0.0.1:ephemeralPort` with `oidc-provider:8080`
|
||||||
|
# so that nginx can access that
|
||||||
|
resolver 127.0.0.11;
|
||||||
|
set $ophost "oidc-provider:8080";
|
||||||
|
proxy_pass http://$ophost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue