-
Notifications
You must be signed in to change notification settings - Fork 25.6k
[Kerberos] Rest client integration test #32070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
bizybot
merged 9 commits into
elastic:feature/kerberos
from
bizybot:kerberos/qakerberos
Jul 18, 2018
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
de23f3f
[Kerberos] Rest client integration test
7263235
Merge branch 'kerberos/sync' into kerberos/qakerberos
b332be8
[Kerberos] Address review comments.
9b8d5ba
[Kerberos] Check if vagrant is available else disable tests
e31628f
[Kerberos] Add missing license header
307d0a4
[Kerberos] Address review comments
25a6b87
[Kerberos] Fix check style error. Forgot to run it.
a923328
[Kerberos] forbidden api check
7488540
[Kerberos] Address review comment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| import java.nio.file.Path | ||
| import java.nio.file.Paths | ||
| import java.nio.file.Files | ||
|
|
||
| apply plugin: 'elasticsearch.vagrantsupport' | ||
| apply plugin: 'elasticsearch.standalone-rest-test' | ||
| apply plugin: 'elasticsearch.rest-test' | ||
|
|
||
| dependencies { | ||
| testCompile project(path: xpackModule('core'), configuration: 'runtime') | ||
| testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') | ||
| testCompile project(path: xpackModule('security'), configuration: 'testArtifacts') | ||
| } | ||
|
|
||
| // MIT Kerberos Vagrant Testing Fixture | ||
| String box = "krb5kdc" | ||
| Map<String,String> vagrantEnvVars = [ | ||
| 'VAGRANT_CWD' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}", | ||
| 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', | ||
| 'VAGRANT_PROJECT_DIR' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}" | ||
| ] | ||
|
|
||
| task krb5kdcUpdate(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { | ||
| command 'box' | ||
| subcommand 'update' | ||
| boxName box | ||
| environmentVars vagrantEnvVars | ||
| dependsOn "vagrantCheckVersion", "virtualboxCheckVersion" | ||
| } | ||
|
|
||
| task krb5kdcFixture(type: org.elasticsearch.gradle.test.VagrantFixture) { | ||
| command 'up' | ||
| args '--provision', '--provider', 'virtualbox' | ||
| boxName box | ||
| environmentVars vagrantEnvVars | ||
| dependsOn krb5kdcUpdate | ||
| } | ||
|
|
||
| task krb5AddPrincipals { dependsOn krb5kdcFixture } | ||
|
|
||
| List<String> principals = [ | ||
| "HTTP/localhost", | ||
| "peppa", | ||
| "george:dino" | ||
| ] | ||
| String realm = "BUILD.ELASTIC.CO" | ||
|
|
||
| for (String principal : principals) { | ||
| String[] princPwdPair = principal.split(':'); | ||
| String princName = princPwdPair[0]; | ||
| String password = ""; | ||
| if (princPwdPair.length > 1) { | ||
| password = princPwdPair[1]; | ||
| } | ||
| Task create = project.tasks.create("addPrincipal#${principal}".replace('/', '_'), org.elasticsearch.gradle.vagrant.VagrantCommandTask) { | ||
| command 'ssh' | ||
| args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $princName $password" | ||
| boxName box | ||
| environmentVars vagrantEnvVars | ||
| dependsOn krb5kdcFixture | ||
| } | ||
| krb5AddPrincipals.dependsOn(create) | ||
| } | ||
|
|
||
| def generatedResources = "$buildDir/generated-resources/keytabs" | ||
| task copyKeytabToGeneratedResources(type: Copy) { | ||
| Path peppaKeytab = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("peppa.keytab").toAbsolutePath() | ||
| from peppaKeytab; | ||
| into generatedResources | ||
| } | ||
|
|
||
| integTestCluster { | ||
| setting 'xpack.license.self_generated.type', 'trial' | ||
| setting 'xpack.security.enabled', 'true' | ||
| setting 'xpack.security.authc.realms.file.type', 'file' | ||
| setting 'xpack.security.authc.realms.file.order', '0' | ||
| setting 'xpack.ml.enabled', 'false' | ||
| setting 'xpack.security.audit.enabled', 'true' | ||
| // Kerberos realm | ||
| setting 'xpack.security.authc.realms.kerberos.type', 'kerberos' | ||
| setting 'xpack.security.authc.realms.kerberos.order', '1' | ||
| setting 'xpack.security.authc.realms.kerberos.keytab.path', 'es.keytab' | ||
| setting 'xpack.security.authc.realms.kerberos.krb.debug', 'true' | ||
| setting 'xpack.security.authc.realms.kerberos.remove_realm_name', 'false' | ||
|
|
||
| Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() | ||
| String jvmArgsStr = " -Djava.security.krb5.conf=${krb5conf}" + " -Dsun.security.krb5.debug=true" | ||
| jvmArgs jvmArgsStr | ||
| Path esKeytab = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("HTTP_localhost.keytab").toAbsolutePath() | ||
| extraConfigFile("es.keytab", "${esKeytab}") | ||
|
|
||
| setupCommand 'setupTestAdmin', | ||
| 'bin/elasticsearch-users', 'useradd', "test_admin", '-p', 'x-pack-test-password', '-r', "superuser" | ||
|
|
||
| waitCondition = { node, ant -> | ||
| File tmpFile = new File(node.cwd, 'wait.success') | ||
| ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=${numNodes}&wait_for_status=yellow", | ||
| dest: tmpFile.toString(), | ||
| username: 'test_admin', | ||
| password: 'x-pack-test-password', | ||
| ignoreerrors: true, | ||
| retries: 10) | ||
| return tmpFile.exists() | ||
| } | ||
|
|
||
| } | ||
|
|
||
| integTestRunner { | ||
| Path peppaKeytab = Paths.get("${project.buildDir}", "generated-resources", "keytabs", "peppa.keytab") | ||
| systemProperty 'test.userkt', "peppa@${realm}" | ||
| systemProperty 'test.userkt.keytab', "${peppaKeytab}" | ||
| systemProperty 'test.userpwd', "george@${realm}" | ||
| systemProperty 'test.userpwd.password', "dino" | ||
| systemProperty 'tests.security.manager', 'true' | ||
| Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() | ||
| List jvmargs = ["-Djava.security.krb5.conf=${krb5conf}","-Dsun.security.krb5.debug=true"] | ||
| jvmArgs jvmargs | ||
| } | ||
|
|
||
| if (project.rootProject.vagrantSupported == false) { | ||
| integTest.enabled = false | ||
| } else { | ||
| project.sourceSets.test.output.dir(generatedResources, builtBy: copyKeytabToGeneratedResources) | ||
| integTestCluster.dependsOn krb5AddPrincipals, krb5kdcFixture, copyKeytabToGeneratedResources | ||
| integTest.finalizedBy project(':test:fixtures:krb5kdc-fixture').halt | ||
| } | ||
152 changes: 152 additions & 0 deletions
152
...c/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosAuthenticationIT.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the Elastic License; | ||
| * you may not use this file except in compliance with the Elastic License. | ||
| */ | ||
|
|
||
| package org.elasticsearch.xpack.security.authc.kerberos; | ||
|
|
||
| import org.apache.http.HttpEntity; | ||
| import org.apache.http.HttpHost; | ||
| import org.elasticsearch.client.Request; | ||
| import org.elasticsearch.client.Response; | ||
| import org.elasticsearch.client.RestClient; | ||
| import org.elasticsearch.client.RestClientBuilder; | ||
| import org.elasticsearch.common.Strings; | ||
| import org.elasticsearch.common.settings.SecureString; | ||
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.common.unit.TimeValue; | ||
| import org.elasticsearch.common.util.concurrent.ThreadContext; | ||
| import org.elasticsearch.common.xcontent.XContentBuilder; | ||
| import org.elasticsearch.common.xcontent.XContentType; | ||
| import org.elasticsearch.test.rest.ESRestTestCase; | ||
| import org.junit.Before; | ||
|
|
||
| import java.io.IOException; | ||
| import java.security.AccessControlContext; | ||
| import java.security.AccessController; | ||
| import java.security.PrivilegedActionException; | ||
| import java.security.PrivilegedExceptionAction; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import javax.security.auth.login.LoginContext; | ||
|
|
||
| import static org.elasticsearch.common.xcontent.XContentHelper.convertToMap; | ||
| import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; | ||
| import static org.hamcrest.Matchers.contains; | ||
| import static org.hamcrest.Matchers.equalTo; | ||
| import static org.hamcrest.Matchers.instanceOf; | ||
|
|
||
| /** | ||
| * Integration test to demonstrate authentication against a real MIT Kerberos | ||
| * instance. | ||
| * <p> | ||
| * Demonstrates login by keytab and login by password for given user principal | ||
| * name using rest client. | ||
| */ | ||
| public class KerberosAuthenticationIT extends ESRestTestCase { | ||
| private static final String ENABLE_KERBEROS_DEBUG_LOGS_KEY = "test.krb.debug"; | ||
| private static final String TEST_USER_WITH_KEYTAB_KEY = "test.userkt"; | ||
| private static final String TEST_USER_WITH_KEYTAB_PATH_KEY = "test.userkt.keytab"; | ||
| private static final String TEST_USER_WITH_PWD_KEY = "test.userpwd"; | ||
| private static final String TEST_USER_WITH_PWD_PASSWD_KEY = "test.userpwd.password"; | ||
| private static final String TEST_KERBEROS_REALM_NAME = "kerberos"; | ||
|
|
||
| @Override | ||
| protected Settings restAdminSettings() { | ||
| final String token = basicAuthHeaderValue("test_admin", new SecureString("x-pack-test-password".toCharArray())); | ||
| return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build(); | ||
| } | ||
|
|
||
| /** | ||
| * Creates simple mapping that maps the users from 'kerberos' realm to | ||
| * the 'kerb_test' role. | ||
| */ | ||
| @Before | ||
| public void setupRoleMapping() throws IOException { | ||
| final String json = Strings // top-level | ||
| .toString(XContentBuilder.builder(XContentType.JSON.xContent()).startObject() | ||
| .array("roles", new String[] { "kerb_test" }) | ||
| .field("enabled", true) | ||
| .startObject("rules") | ||
| .startArray("all") | ||
| .startObject().startObject("field").field("realm.name", TEST_KERBEROS_REALM_NAME).endObject().endObject() | ||
| .endArray() // "all" | ||
| .endObject() // "rules" | ||
| .endObject()); | ||
|
|
||
| final Request request = new Request("POST", "/_xpack/security/role_mapping/kerberosrolemapping"); | ||
| request.setJsonEntity(json); | ||
| final Response response = adminClient().performRequest(request); | ||
| assertOK(response); | ||
| } | ||
|
|
||
| public void testLoginByKeytab() throws IOException, PrivilegedActionException { | ||
| final String userPrincipalName = System.getProperty(TEST_USER_WITH_KEYTAB_KEY); | ||
| final String keytabPath = System.getProperty(TEST_USER_WITH_KEYTAB_PATH_KEY); | ||
| final boolean enabledDebugLogs = Boolean.parseBoolean(System.getProperty(ENABLE_KERBEROS_DEBUG_LOGS_KEY)); | ||
| final SpnegoHttpClientConfigCallbackHandler callbackHandler = new SpnegoHttpClientConfigCallbackHandler(userPrincipalName, | ||
| keytabPath, enabledDebugLogs); | ||
| executeRequestAndVerifyResponse(userPrincipalName, callbackHandler); | ||
| } | ||
|
|
||
| public void testLoginByUsernamePassword() throws IOException, PrivilegedActionException { | ||
| final String userPrincipalName = System.getProperty(TEST_USER_WITH_PWD_KEY); | ||
| final String password = System.getProperty(TEST_USER_WITH_PWD_PASSWD_KEY); | ||
| final boolean enabledDebugLogs = Boolean.parseBoolean(System.getProperty(ENABLE_KERBEROS_DEBUG_LOGS_KEY)); | ||
| final SpnegoHttpClientConfigCallbackHandler callbackHandler = new SpnegoHttpClientConfigCallbackHandler(userPrincipalName, | ||
| new SecureString(password.toCharArray()), enabledDebugLogs); | ||
| executeRequestAndVerifyResponse(userPrincipalName, callbackHandler); | ||
| } | ||
|
|
||
| private void executeRequestAndVerifyResponse(final String userPrincipalName, | ||
| final SpnegoHttpClientConfigCallbackHandler callbackHandler) throws PrivilegedActionException, IOException { | ||
| final Request request = new Request("GET", "/_xpack/security/_authenticate"); | ||
| try (RestClient restClient = buildRestClientForKerberos(callbackHandler)) { | ||
| final AccessControlContext accessControlContext = AccessController.getContext(); | ||
| final LoginContext lc = callbackHandler.login(); | ||
| Response response = SpnegoHttpClientConfigCallbackHandler.doAsPrivilegedWrapper(lc.getSubject(), | ||
| (PrivilegedExceptionAction<Response>) () -> { | ||
| return restClient.performRequest(request); | ||
| }, accessControlContext); | ||
|
|
||
| assertOK(response); | ||
| final Map<String, Object> map = parseResponseAsMap(response.getEntity()); | ||
| assertThat(map.get("username"), equalTo(userPrincipalName)); | ||
| assertThat(map.get("roles"), instanceOf(List.class)); | ||
| assertThat(((List<?>) map.get("roles")), contains("kerb_test")); | ||
| } | ||
| } | ||
|
|
||
| private Map<String, Object> parseResponseAsMap(final HttpEntity entity) throws IOException { | ||
| return convertToMap(XContentType.JSON.xContent(), entity.getContent(), false); | ||
| } | ||
|
|
||
| private RestClient buildRestClientForKerberos(final SpnegoHttpClientConfigCallbackHandler callbackHandler) throws IOException { | ||
| final Settings settings = restAdminSettings(); | ||
| final HttpHost[] hosts = getClusterHosts().toArray(new HttpHost[getClusterHosts().size()]); | ||
|
|
||
| final RestClientBuilder restClientBuilder = RestClient.builder(hosts); | ||
| configureRestClientBuilder(restClientBuilder, settings); | ||
| restClientBuilder.setHttpClientConfigCallback(callbackHandler); | ||
| return restClientBuilder.build(); | ||
| } | ||
|
|
||
| private static void configureRestClientBuilder(final RestClientBuilder restClientBuilder, final Settings settings) | ||
| throws IOException { | ||
| final String requestTimeoutString = settings.get(CLIENT_RETRY_TIMEOUT); | ||
| if (requestTimeoutString != null) { | ||
| final TimeValue maxRetryTimeout = TimeValue.parseTimeValue(requestTimeoutString, CLIENT_RETRY_TIMEOUT); | ||
| restClientBuilder.setMaxRetryTimeoutMillis(Math.toIntExact(maxRetryTimeout.getMillis())); | ||
| } | ||
| final String socketTimeoutString = settings.get(CLIENT_SOCKET_TIMEOUT); | ||
| if (socketTimeoutString != null) { | ||
| final TimeValue socketTimeout = TimeValue.parseTimeValue(socketTimeoutString, CLIENT_SOCKET_TIMEOUT); | ||
| restClientBuilder.setRequestConfigCallback(conf -> conf.setSocketTimeout(Math.toIntExact(socketTimeout.getMillis()))); | ||
| } | ||
| if (settings.hasValue(CLIENT_PATH_PREFIX)) { | ||
| restClientBuilder.setPathPrefix(settings.get(CLIENT_PATH_PREFIX)); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we specify debug in the realm and in the jvm args? Is it because our debug value overrides the system property? If so, we need to change the default of the setting to be the value of the system property.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two flags for debug level logs,
-Djava.security.krb5.confenables debug logs at the Kerberos protocol level. The flag that we take in realm config is only for Krb5LoginModule from JAAS. I will add this information to the debugging and troubleshooting documentation. Thank you.