Run active directory tests against a samba4 fixture (elastic/x-pack-elasticsearch#4067)

This commit adds a Samba4 test fixture that acts as a domain controller
and has the same contents as the cloud active directory instance that
we previously used for tests.

The tests also support reading information from environment variables
so that they can be run against a real active directory instance in our
CI builds.

In addition, this commit also fixes a few issues that surfaced when
making this change. The first is a change in the base DN that is
searched when performing down-level authentication. The base DN is
now the configuration object instead of the domain DN. This change was
required due to the original producing unnecessary referrals, which we
cannot easily follow when running against this test figure. Referrals
cannot easily be followed as they are returned by the ldap server with
an unresolvable DNS name unless the host points to the samba4 instance
for DNS. The port returned in the referral url is the one samba is bound
to, which differs from the port that is forwarded to the host by the
test fixture.

The other issue that is resolved by this change is the addition of
settings that allow specifying non-standard ports for active directory.
This is needed for down-level authentication as we may need to query
the regular port of active directory instead of the global catalog
port as the configuration object is not replicated to the global
catalog.

relates elastic/x-pack-elasticsearch#185
Relates elastic/x-pack-elasticsearch#3800

Original commit: elastic/x-pack-elasticsearch@883c742fba
This commit is contained in:
Jay Modi 2018-03-16 10:44:23 -06:00 committed by GitHub
parent 4c78ede9c1
commit bccf988e9d
28 changed files with 478 additions and 191 deletions

View File

@ -20,6 +20,10 @@ public final class ActiveDirectorySessionFactorySettings {
public static final String AD_UPN_USER_SEARCH_FILTER_SETTING = "user_search.upn_filter";
public static final String AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING = "user_search.down_level_filter";
public static final String AD_USER_SEARCH_SCOPE_SETTING = "user_search.scope";
public static final Setting<Integer> AD_LDAP_PORT_SETTING = Setting.intSetting("port.ldap", 389, Setting.Property.NodeScope);
public static final Setting<Integer> AD_LDAPS_PORT_SETTING = Setting.intSetting("port.ldaps", 636, Setting.Property.NodeScope);
public static final Setting<Integer> AD_GC_LDAP_PORT_SETTING = Setting.intSetting("port.gc_ldap", 3268, Setting.Property.NodeScope);
public static final Setting<Integer> AD_GC_LDAPS_PORT_SETTING = Setting.intSetting("port.gc_ldaps", 3269, Setting.Property.NodeScope);
public static final Setting<Boolean> POOL_ENABLED = Setting.boolSetting("user_search.pool.enabled",
settings -> Boolean.toString(PoolingSessionFactorySettings.BIND_DN.exists(settings)), Setting.Property.NodeScope);
@ -36,6 +40,10 @@ public final class ActiveDirectorySessionFactorySettings {
settings.add(Setting.simpleString(AD_UPN_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, Setting.Property.NodeScope));
settings.add(Setting.simpleString(AD_USER_SEARCH_SCOPE_SETTING, Setting.Property.NodeScope));
settings.add(AD_LDAP_PORT_SETTING);
settings.add(AD_LDAPS_PORT_SETTING);
settings.add(AD_GC_LDAP_PORT_SETTING);
settings.add(AD_GC_LDAPS_PORT_SETTING);
settings.add(POOL_ENABLED);
settings.addAll(PoolingSessionFactorySettings.getSettings());
return settings;

View File

@ -128,7 +128,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
assertThat(aliases[0], is("key"));
};
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(4));
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5));
};
validateSSLConfigurationIsReloaded(settings, env, keyManagerPreChecks, trustManagerPreChecks, modifier, keyManagerPostChecks,
trustManagerPostChecks);
@ -244,7 +244,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
};
final BiConsumer<X509ExtendedTrustManager, SSLConfiguration> trustManagerPostChecks = (updatedTrustManager, config) -> {
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(5));
assertThat(trustedCount.get() - updatedTrustManager.getAcceptedIssuers().length, is(6));
};
validateTrustConfigurationIsReloaded(settings, env, trustManagerPreChecks, modifier, trustManagerPostChecks);

View File

@ -485,7 +485,7 @@ public class SSLServiceTests extends ESTestCase {
final SSLService sslService = new SSLService(settings, env);
final List<CertificateInfo> certificates = new ArrayList<>(sslService.getLoadedCertificates());
assertThat(certificates, iterableWithSize(7));
assertThat(certificates, iterableWithSize(8));
Collections.sort(certificates,
Comparator.comparing((CertificateInfo c) -> c.alias() == null ? "" : c.alias()).thenComparing(CertificateInfo::path));
@ -508,6 +508,15 @@ public class SSLServiceTests extends ESTestCase {
assertThat(cert.expiry(), equalTo(DateTime.parse("2029-08-27T16:32:42Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("mykey"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("3151a81eec8d4e34c56a8466a8510bcfbe63cc31"));
assertThat(cert.subjectDn(), equalTo("CN=samba4"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2021-02-14T17:49:11.000Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("openldap"));
assertThat(cert.path(), equalTo(jksPath.toString()));

View File

@ -62,6 +62,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
final DownLevelADAuthenticator downLevelADAuthenticator;
final UpnADAuthenticator upnADAuthenticator;
private final int ldapPort;
ActiveDirectorySessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool) throws LDAPException {
super(config, sslService, new ActiveDirectoryGroupsResolver(config.settings()),
ActiveDirectorySessionFactorySettings.POOL_ENABLED, () -> {
@ -88,10 +90,15 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
+ "] setting for active directory");
}
String domainDN = buildDnFromDomain(domainName);
ldapPort = ActiveDirectorySessionFactorySettings.AD_LDAP_PORT_SETTING.get(settings);
final int ldapsPort = ActiveDirectorySessionFactorySettings.AD_LDAPS_PORT_SETTING.get(settings);
final int gcLdapPort = ActiveDirectorySessionFactorySettings.AD_GC_LDAP_PORT_SETTING.get(settings);
final int gcLdapsPort = ActiveDirectorySessionFactorySettings.AD_GC_LDAPS_PORT_SETTING.get(settings);
defaultADAuthenticator = new DefaultADAuthenticator(config, timeout, ignoreReferralErrors, logger, groupResolver,
metaDataResolver, domainDN, threadPool);
downLevelADAuthenticator = new DownLevelADAuthenticator(config, timeout, ignoreReferralErrors, logger, groupResolver,
metaDataResolver, domainDN, sslService, threadPool);
metaDataResolver, domainDN, sslService, threadPool, ldapPort, ldapsPort, gcLdapPort, gcLdapsPort);
upnADAuthenticator = new UpnADAuthenticator(config, timeout, ignoreReferralErrors, logger, groupResolver,
metaDataResolver, domainDN, threadPool);
@ -99,7 +106,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
@Override
protected List<String> getDefaultLdapUrls(Settings settings) {
return Collections.singletonList("ldap://" + settings.get(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING) + ":389");
return Collections.singletonList("ldap://" + settings.get(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING) +
":" + ldapPort);
}
@Override
@ -361,16 +369,24 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
final Settings settings;
final SSLService sslService;
final RealmConfig config;
private final int ldapPort;
private final int ldapsPort;
private final int gcLdapPort;
private final int gcLdapsPort;
DownLevelADAuthenticator(RealmConfig config, TimeValue timeout, boolean ignoreReferralErrors, Logger logger,
GroupsResolver groupsResolver, LdapMetaDataResolver metaDataResolver, String domainDN, SSLService sslService,
ThreadPool threadPool) {
ThreadPool threadPool, int ldapPort, int ldapsPort, int gcLdapPort, int gcLdapsPort) {
super(config, timeout, ignoreReferralErrors, logger, groupsResolver, metaDataResolver, domainDN,
ActiveDirectorySessionFactorySettings.AD_DOWN_LEVEL_USER_SEARCH_FILTER_SETTING, DOWN_LEVEL_FILTER, threadPool);
this.domainDN = domainDN;
this.settings = config.settings();
this.sslService = sslService;
this.config = config;
this.ldapPort = ldapPort;
this.ldapsPort = ldapsPort;
this.gcLdapPort = gcLdapPort;
this.gcLdapsPort = gcLdapsPort;
}
@Override
@ -400,7 +416,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
if (cachedName != null) {
listener.onResponse(cachedName);
} else if (usingGlobalCatalog(ldapInterface) == false) {
search(ldapInterface, domainDN, LdapSearchScope.SUB_TREE.scope(), filter, timeLimitSeconds, ignoreReferralErrors,
search(ldapInterface, "CN=Configuration," + domainDN, LdapSearchScope.SUB_TREE.scope(), filter, timeLimitSeconds,
ignoreReferralErrors,
ActionListener.wrap((results) -> handleSearchResults(results, netBiosDomainName, domainNameCache, listener),
listener::onFailure),
"ncname");
@ -417,7 +434,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
final LDAPConnection finalLdapConnection = ldapConnection;
final LDAPConnection searchConnection = LdapUtils.privilegedConnect(
() -> new LDAPConnection(finalLdapConnection.getSocketFactory(), connectionOptions(config, sslService, logger),
finalLdapConnection.getConnectedAddress(), finalLdapConnection.getSSLSession() != null ? 636 : 389));
finalLdapConnection.getConnectedAddress(),
finalLdapConnection.getSSLSession() != null ? ldapsPort : ldapPort));
final byte[] passwordBytes = CharArrays.toUtf8Bytes(password.getChars());
final SimpleBindRequest bind = bindDN.isEmpty()
? new SimpleBindRequest(username, passwordBytes)
@ -425,8 +443,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
LdapUtils.maybeForkThenBind(searchConnection, bind, threadPool, new ActionRunnable<String>(listener) {
@Override
protected void doRun() throws Exception {
search(searchConnection, domainDN, LdapSearchScope.SUB_TREE.scope(), filter, timeLimitSeconds,
ignoreReferralErrors,
search(searchConnection, "CN=Configuration," + domainDN, LdapSearchScope.SUB_TREE.scope(), filter,
timeLimitSeconds, ignoreReferralErrors,
ActionListener.wrap(
results -> {
IOUtils.close(searchConnection);
@ -473,7 +491,7 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
}
}
static boolean usingGlobalCatalog(LDAPInterface ldap) throws LDAPException {
boolean usingGlobalCatalog(LDAPInterface ldap) throws LDAPException {
if (ldap instanceof LDAPConnection) {
return usingGlobalCatalog((LDAPConnection) ldap);
} else {
@ -490,8 +508,8 @@ class ActiveDirectorySessionFactory extends PoolingSessionFactory {
}
}
private static boolean usingGlobalCatalog(LDAPConnection ldapConnection) {
return ldapConnection.getConnectedPort() == 3268 || ldapConnection.getConnectedPort() == 3269;
private boolean usingGlobalCatalog(LDAPConnection ldapConnection) {
return ldapConnection.getConnectedPort() == gcLdapPort || ldapConnection.getConnectedPort() == gcLdapsPort;
}
}

View File

@ -13,7 +13,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.concurrent.UncategorizedExecutionException;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@ -28,12 +27,10 @@ import org.junit.Before;
import java.util.List;
import java.util.concurrent.ExecutionException;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
public class LdapSessionFactoryTests extends LdapTestCase {
private Settings globalSettings;

View File

@ -44,7 +44,7 @@ import java.util.Objects;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.HOSTNAME_VERIFICATION_SETTING;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.URLS_SETTING;
//
public abstract class LdapTestCase extends ESTestCase {
private static final String USER_DN_TEMPLATES_SETTING_KEY = LdapSessionFactorySettings.USER_DN_TEMPLATES_SETTING.getKey();
@ -120,8 +120,7 @@ public abstract class LdapTestCase extends ESTestCase {
.put(SessionFactorySettings.TIMEOUT_TCP_CONNECTION_SETTING, TimeValue.timeValueSeconds(1L))
.put(SessionFactorySettings.IGNORE_REFERRAL_ERRORS_SETTING.getKey(), ignoreReferralErrors)
.put("group_search.base_dn", groupSearchBase)
.put("group_search.scope", scope)
.put("ssl.verification_mode", VerificationMode.CERTIFICATE);
.put("group_search.scope", scope);
if (serverSetType != null) {
builder.put(LdapLoadBalancingSettings.LOAD_BALANCE_SETTINGS + "." +
LdapLoadBalancingSettings.LOAD_BALANCE_TYPE_SETTING, serverSetType.toString());

View File

@ -21,7 +21,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@SuppressWarnings("unchecked")
public class SearchGroupsResolverTests extends GroupsResolverTestCase {
public class SearchGroupsResolverTests extends GroupsResolverTestCase {
private static final String BRUCE_BANNER_DN = "uid=hulk,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";

View File

@ -1,3 +1,7 @@
Project smbFixtureProject = xpackProject("test:smb-fixture")
evaluationDependsOn(smbFixtureProject.path)
apply plugin: 'elasticsearch.vagrantsupport'
apply plugin: 'elasticsearch.standalone-test'
dependencies {
@ -5,10 +9,11 @@ dependencies {
testCompile project(path: xpackModule('security'), configuration: 'testArtifacts')
}
// add test resources from security, so certificate tool tests can use example certs
// add test resources from security, so tests can use example certs
sourceSets.test.resources.srcDirs(project(xpackModule('security')).sourceSets.test.resources.srcDirs)
compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
// we have to repeate these patterns because the security test resources are effectively in the src of this project
// we have to repeat these patterns because the security test resources are effectively in the src of this project
forbiddenPatterns {
exclude '**/*.key'
exclude '**/*.p12'
@ -21,7 +26,22 @@ test {
* other if we allow them to set the number of available processors as it's set-once in Netty.
*/
systemProperty 'es.set.netty.runtime.available.processors', 'false'
include '**/*IT.class'
include '**/*Tests.class'
}
// these are just tests, no need to audit
thirdPartyAudit.enabled = false
task smbFixture {
dependsOn "vagrantCheckVersion", "virtualboxCheckVersion", smbFixtureProject.up
}
if (project.rootProject.vagrantSupported) {
if (project.hasProperty('useExternalAD') == false) {
test.dependsOn smbFixture
test.finalizedBy smbFixtureProject.halt
}
} else {
test.enabled = false
}

View File

@ -5,7 +5,7 @@
*/
package org.elasticsearch.xpack.security.authc.ldap;
import com.unboundid.ldap.sdk.LDAPException;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.SecureString;
@ -13,7 +13,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@ -21,17 +20,19 @@ import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapTestCase;
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.junit.After;
import org.junit.Before;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.xpack.security.authc.ldap.LdapUserSearchSessionFactoryTests.getLdapUserSearchSessionFactory;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItems;
public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
public class ADLdapUserSearchSessionFactoryTests extends AbstractActiveDirectoryTestCase {
private SSLService sslService;
private Settings globalSettings;
@ -53,7 +54,7 @@ public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
.setSecureSettings(newSecureSettings("xpack.ssl.truststore.secure_password", "changeit"))
.build();
sslService = new SSLService(globalSettings, env);
threadPool = new TestThreadPool("LdapUserSearchSessionFactoryTests");
threadPool = new TestThreadPool("ADLdapUserSearchSessionFactoryTests");
}
@After
@ -61,8 +62,12 @@ public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
terminate(threadPool);
}
@Network
@SuppressWarnings("unchecked")
private MockSecureSettings newSecureSettings(String key, String value) {
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString(key, value);
return secureSettings;
}
public void testUserSearchWithActiveDirectory() throws Exception {
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userSearchBase = "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
@ -76,6 +81,7 @@ public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
.put("bind_password", ActiveDirectorySessionFactoryTests.PASSWORD)
.put("user_search.filter", "(cn={0})")
.put("user_search.pool.enabled", randomBoolean())
.put("follow_referrals", ActiveDirectorySessionFactoryTests.FOLLOW_REFERRALS)
.build();
Settings.Builder builder = Settings.builder()
.put(globalSettings);
@ -85,8 +91,8 @@ public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
});
Settings fullSettings = builder.build();
sslService = new SSLService(fullSettings, TestEnvironment.newEnvironment(fullSettings));
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings),
new ThreadContext(globalSettings));
LdapUserSearchSessionFactory sessionFactory = getLdapUserSearchSessionFactory(config, sslService, threadPool);
String user = "Bruce Banner";
@ -119,20 +125,27 @@ public class ActiveDirectoryUserSearchSessionFactoryTests extends LdapTestCase {
}
}
static LdapUserSearchSessionFactory getLdapUserSearchSessionFactory(RealmConfig config, SSLService sslService, ThreadPool threadPool)
throws LDAPException {
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, sslService, threadPool);
if (sessionFactory.getConnectionPool() != null) {
// don't use this in production
// used here to catch bugs that might get masked by an automatic retry
sessionFactory.getConnectionPool().setRetryFailedOperationsDueToInvalidConnections(false);
}
return sessionFactory;
@Override
protected boolean enableWarningsCheck() {
return false;
}
private MockSecureSettings newSecureSettings(String key, String value) {
MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString(key, value);
return secureSettings;
private LdapSession session(SessionFactory factory, String username, SecureString password) {
PlainActionFuture<LdapSession> future = new PlainActionFuture<>();
factory.session(username, password, future);
return future.actionGet();
}
private List<String> groups(LdapSession ldapSession) {
Objects.requireNonNull(ldapSession);
PlainActionFuture<List<String>> future = new PlainActionFuture<>();
ldapSession.groups(future);
return future.actionGet();
}
private LdapSession unauthenticatedSession(SessionFactory factory, String username) {
PlainActionFuture<LdapSession> future = new PlainActionFuture<>();
factory.unauthenticatedSession(username, future);
return future.actionGet();
}
}

View File

@ -11,11 +11,11 @@ import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPInterface;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.core.security.authc.ldap.ActiveDirectorySessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings;
@ -27,11 +27,22 @@ import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
@Network
public class AbstractActiveDirectoryIntegTests extends ESTestCase {
public abstract class AbstractActiveDirectoryTestCase extends ESTestCase {
public static final String AD_LDAP_URL = "ldaps://54.213.145.20:636";
public static final String PASSWORD = "NickFuryHeartsES";
// follow referrals defaults to false here which differs from the default value of the setting
// this is needed to prevent test logs being filled by errors as the default configuration of
// the tests run against a vagrant samba4 instance configured as a domain controller with the
// ports mapped into the ephemeral port range and there is the possibility of incorrect results
// as we cannot control the URL of the referral which may contain a non-resolvable DNS name as
// this name would be served by the samba4 instance
public static final Boolean FOLLOW_REFERRALS = Booleans.parseBoolean(getFromEnv("TESTS_AD_FOLLOW_REFERRALS", "false"));
public static final String AD_LDAP_URL = getFromEnv("TESTS_AD_LDAP_URL", "ldaps://localhost:61636");
public static final String AD_LDAP_GC_URL = getFromEnv("TESTS_AD_LDAP_GC_URL", "ldaps://localhost:63269");
public static final String PASSWORD = getFromEnv("TESTS_AD_USER_PASSWORD", "Passw0rd");
public static final String AD_LDAP_PORT = getFromEnv("TESTS_AD_LDAP_PORT", "61389");
public static final String AD_LDAPS_PORT = getFromEnv("TESTS_AD_LDAPS_PORT", "61636");
public static final String AD_GC_LDAP_PORT = getFromEnv("TESTS_AD_GC_LDAP_PORT", "63268");
public static final String AD_GC_LDAPS_PORT = getFromEnv("TESTS_AD_GC_LDAPS_PORT", "63269");
public static final String AD_DOMAIN = "ad.test.elasticsearch.com";
protected SSLService sslService;
@ -76,7 +87,12 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
.putList(SessionFactorySettings.URLS_SETTING, ldapUrl)
.put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_BASEDN_SETTING, userSearchDN)
.put(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_SCOPE_SETTING, scope);
.put(ActiveDirectorySessionFactorySettings.AD_USER_SEARCH_SCOPE_SETTING, scope)
.put(ActiveDirectorySessionFactorySettings.AD_LDAP_PORT_SETTING.getKey(), AD_LDAP_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_LDAPS_PORT_SETTING.getKey(), AD_LDAPS_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_GC_LDAP_PORT_SETTING.getKey(), AD_GC_LDAP_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_GC_LDAPS_PORT_SETTING.getKey(), AD_GC_LDAPS_PORT)
.put("follow_referrals", FOLLOW_REFERRALS);
if (randomBoolean()) {
builder.put("ssl.verification_mode", hostnameVerification ? VerificationMode.FULL : VerificationMode.CERTIFICATE);
} else {
@ -109,4 +125,9 @@ public class AbstractActiveDirectoryIntegTests extends ESTestCase {
}
});
}
private static String getFromEnv(String envVar, String defaultValue) {
final String value = System.getenv(envVar);
return value == null ? defaultValue : value;
}
}

View File

@ -3,7 +3,7 @@
* 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.integration.ldap;
package org.elasticsearch.xpack.security.authc.ldap;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionFuture;
@ -25,6 +25,7 @@ import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.core.security.SecurityLifecycleServiceField;
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRequestBuilder;
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingResponse;
import org.elasticsearch.xpack.core.security.authc.ldap.ActiveDirectorySessionFactorySettings;
import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.client.SecurityClient;
@ -53,6 +54,10 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope.ONE_LEVEL;
import static org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope.SUB_TREE;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
import static org.elasticsearch.xpack.security.authc.ldap.AbstractActiveDirectoryTestCase.AD_GC_LDAPS_PORT;
import static org.elasticsearch.xpack.security.authc.ldap.AbstractActiveDirectoryTestCase.AD_GC_LDAP_PORT;
import static org.elasticsearch.xpack.security.authc.ldap.AbstractActiveDirectoryTestCase.AD_LDAPS_PORT;
import static org.elasticsearch.xpack.security.authc.ldap.AbstractActiveDirectoryTestCase.AD_LDAP_PORT;
import static org.elasticsearch.xpack.security.test.SecurityTestUtils.writeFile;
import static org.hamcrest.Matchers.equalTo;
@ -63,7 +68,7 @@ import static org.hamcrest.Matchers.equalTo;
public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase {
public static final String XPACK_SECURITY_AUTHC_REALMS_EXTERNAL = "xpack.security.authc.realms.external";
public static final String PASSWORD = "NickFuryHeartsES";
public static final String PASSWORD = AbstractActiveDirectoryTestCase.PASSWORD;
public static final String ASGARDIAN_INDEX = "gods";
public static final String PHILANTHROPISTS_INDEX = "philanthropists";
public static final String SECURITY_INDEX = "security";
@ -119,43 +124,39 @@ public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase
final RealmConfig realm = AbstractAdLdapRealmTestCase.realmConfig;
Path store = getDataPath(TESTNODE_KEYSTORE);
Settings.Builder builder = Settings.builder();
if (useGlobalSSL) {
// don't use filter since it returns a prefixed secure setting instead of mock!
Settings settingsToAdd = super.nodeSettings(nodeOrdinal);
builder.put(settingsToAdd.filter(k -> k.startsWith("xpack.ssl.") == false), false);
MockSecureSettings mockSecureSettings = (MockSecureSettings) Settings.builder().put(settingsToAdd).getSecureSettings();
if (mockSecureSettings != null) {
MockSecureSettings filteredSecureSettings = new MockSecureSettings();
builder.setSecureSettings(filteredSecureSettings);
for (String secureSetting : mockSecureSettings.getSettingNames()) {
if (secureSetting.startsWith("xpack.ssl.") == false) {
SecureString secureString = mockSecureSettings.getString(secureSetting);
if (secureString == null) {
final byte[] fileBytes;
try (InputStream in = mockSecureSettings.getFile(secureSetting);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
int numRead;
byte[] bytes = new byte[1024];
while ((numRead = in.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes, 0, numRead);
}
byteArrayOutputStream.flush();
fileBytes = byteArrayOutputStream.toByteArray();
} catch (IOException e) {
throw new UncheckedIOException(e);
// don't use filter since it returns a prefixed secure setting instead of mock!
Settings settingsToAdd = super.nodeSettings(nodeOrdinal);
builder.put(settingsToAdd.filter(k -> k.startsWith("xpack.ssl.") == false), false);
MockSecureSettings mockSecureSettings = (MockSecureSettings) Settings.builder().put(settingsToAdd).getSecureSettings();
if (mockSecureSettings != null) {
MockSecureSettings filteredSecureSettings = new MockSecureSettings();
builder.setSecureSettings(filteredSecureSettings);
for (String secureSetting : mockSecureSettings.getSettingNames()) {
if (secureSetting.startsWith("xpack.ssl.") == false) {
SecureString secureString = mockSecureSettings.getString(secureSetting);
if (secureString == null) {
final byte[] fileBytes;
try (InputStream in = mockSecureSettings.getFile(secureSetting);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
int numRead;
byte[] bytes = new byte[1024];
while ((numRead = in.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes, 0, numRead);
}
filteredSecureSettings.setFile(secureSetting, fileBytes);
} else {
filteredSecureSettings.setString(secureSetting, new String(secureString.getChars()));
byteArrayOutputStream.flush();
fileBytes = byteArrayOutputStream.toByteArray();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
filteredSecureSettings.setFile(secureSetting, fileBytes);
} else {
filteredSecureSettings.setString(secureSetting, new String(secureString.getChars()));
}
}
}
addSslSettingsForStore(builder, store, "testnode");
} else {
builder.put(super.nodeSettings(nodeOrdinal));
}
addSslSettingsForStore(builder, store, "testnode");
builder.put(buildRealmSettings(realm, roleMappings, store));
return builder.build();
}
@ -376,39 +377,44 @@ public abstract class AbstractAdLdapRealmTestCase extends SecurityIntegTestCase
AD(false, AD_ROLE_MAPPING,
Settings.builder()
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".type", LdapRealmSettings.AD_TYPE)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".domain_name", "ad.test.elasticsearch.com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".domain_name", ActiveDirectorySessionFactoryTests.AD_DOMAIN)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL
+ ".group_search.base_dn", "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".group_search.scope", randomBoolean() ? SUB_TREE : ONE_LEVEL)
.build()),
AD_SSL(false, AD_ROLE_MAPPING,
Settings.builder()
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".type", LdapRealmSettings.AD_TYPE)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".domain_name", "ad.test.elasticsearch.com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL
+ ".group_search.base_dn", "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".group_search.scope", randomBoolean() ? SUB_TREE : ONE_LEVEL)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", "ldap://ad.test.elasticsearch.com:389")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", ActiveDirectorySessionFactoryTests.AD_LDAP_URL)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".follow_referrals",
ActiveDirectorySessionFactoryTests.FOLLOW_REFERRALS)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + "." +
ActiveDirectorySessionFactorySettings.AD_LDAP_PORT_SETTING.getKey(), AD_LDAP_PORT)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + "." +
ActiveDirectorySessionFactorySettings.AD_LDAPS_PORT_SETTING.getKey(), AD_LDAPS_PORT)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + "." +
ActiveDirectorySessionFactorySettings.AD_GC_LDAP_PORT_SETTING.getKey(), AD_GC_LDAP_PORT)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + "." +
ActiveDirectorySessionFactorySettings.AD_GC_LDAPS_PORT_SETTING.getKey(), AD_GC_LDAPS_PORT)
.build()),
AD_LDAP_GROUPS_FROM_SEARCH(true, AD_ROLE_MAPPING,
Settings.builder()
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".type", LdapRealmSettings.LDAP_TYPE)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", "ldaps://ad.test.elasticsearch.com:636")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", ActiveDirectorySessionFactoryTests.AD_LDAP_URL)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL
+ ".group_search.base_dn", "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".group_search.scope", randomBoolean() ? SUB_TREE : ONE_LEVEL)
.putList(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".user_dn_templates",
"cn={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".follow_referrals",
ActiveDirectorySessionFactoryTests.FOLLOW_REFERRALS)
.build()),
AD_LDAP_GROUPS_FROM_ATTRIBUTE(true, AD_ROLE_MAPPING,
Settings.builder()
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".type", LdapRealmSettings.LDAP_TYPE)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", "ldaps://ad.test.elasticsearch.com:636")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".url", ActiveDirectorySessionFactoryTests.AD_LDAP_URL)
.putList(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".user_dn_templates",
"cn={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".follow_referrals",
ActiveDirectorySessionFactoryTests.FOLLOW_REFERRALS)
.build());
final boolean mapGroupsAsRoles;

View File

@ -9,9 +9,9 @@ import com.unboundid.ldap.sdk.Filter;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope;
import org.elasticsearch.xpack.core.security.support.NoOpLogger;
import org.junit.Before;
import java.util.List;
import java.util.regex.Pattern;
@ -21,12 +21,16 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
@Network
public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
private static final String BRUCE_BANNER_DN =
"cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
@Before
public void setReferralFollowing() {
ldapConnection.getConnectionOptions().setFollowReferrals(AbstractActiveDirectoryTestCase.FOLLOW_REFERRALS);
}
@SuppressWarnings("unchecked")
public void testResolveSubTree() throws Exception {
Settings settings = Settings.builder()
@ -76,7 +80,6 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users group
"S-1-5-21-3510024162-210737641-214529065-513" //Default Domain Users group
};
final String dn = "CN=Jarvis, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com";
PlainActionFuture<Filter> future = new PlainActionFuture<>();
@ -89,9 +92,8 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
//test a user of one groups
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users group
"S-1-5-21-3510024162-210737641-214529065-513", //Default Domain Users group
"S-1-5-21-3510024162-210737641-214529065-1117"}; //Gods group
"S-1-5-32-545" //Default Users group
};
final String dn = "CN=Odin, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com";
PlainActionFuture<Filter> future = new PlainActionFuture<>();
ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection, dn,
@ -99,25 +101,6 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
Filter query = future.actionGet();
assertValidSidQuery(query, expectedSids);
}
//test a user of many groups
{
String[] expectedSids = new String[]{
"S-1-5-32-545", //Default Users Group
"S-1-5-21-3510024162-210737641-214529065-513", //Default Domain Users group
"S-1-5-21-3510024162-210737641-214529065-1123", //Supers
"S-1-5-21-3510024162-210737641-214529065-1110", //Philanthropists
"S-1-5-21-3510024162-210737641-214529065-1108", //Geniuses
"S-1-5-21-3510024162-210737641-214529065-1106", //SHIELD
"S-1-5-21-3510024162-210737641-214529065-1105"};//Avengers
final String dn = BRUCE_BANNER_DN;
PlainActionFuture<Filter> future = new PlainActionFuture<>();
ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection, dn,
TimeValue.timeValueSeconds(10), false, future);
Filter query = future.actionGet();
assertValidSidQuery(query, expectedSids);
}
}
private void assertValidSidQuery(Filter query, String[] expectedSids) {
@ -125,7 +108,7 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
Pattern sidQueryPattern = Pattern.compile("\\(\\|(\\(objectSid=S(-\\d+)+\\))+\\)");
assertThat("[" + queryString + "] didn't match the search filter pattern",
sidQueryPattern.matcher(queryString).matches(), is(true));
for(String sid: expectedSids) {
for (String sid: expectedSids) {
assertThat(queryString, containsString(sid));
}
}

View File

@ -3,20 +3,18 @@
* 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.integration.ldap;
package org.elasticsearch.xpack.security.authc.ldap;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
import org.elasticsearch.xpack.core.security.authc.AuthenticationServiceField;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.security.authc.ldap.ActiveDirectorySessionFactoryTests;
import org.hamcrest.Matchers;
import org.junit.BeforeClass;
@ -28,12 +26,11 @@ import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswo
/**
* This tests that "run-as" works on LDAP/AD realms
*/
@Network
public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
@BeforeClass
public static void selectRealmConfig() {
realmConfig = randomFrom(RealmConfig.AD, RealmConfig.AD_SSL);
realmConfig = RealmConfig.AD;
}
@Override
@ -41,7 +38,6 @@ public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
final Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal));
switch (realmConfig) {
case AD:
case AD_SSL:
builder.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_dn", "ironman@ad.test.elasticsearch.com")
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".bind_password", ActiveDirectorySessionFactoryTests.PASSWORD)
.put(XPACK_SECURITY_AUTHC_REALMS_EXTERNAL + ".user_search.pool.enabled", false);

View File

@ -13,7 +13,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.concurrent.UncategorizedExecutionException;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@ -39,8 +38,7 @@ import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@Network
public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryIntegTests {
public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryTestCase {
private final SecureString SECURED_PASSWORD = new SecureString(PASSWORD);
private ThreadPool threadPool;
@ -87,7 +85,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
@SuppressWarnings("unchecked")
public void testNetbiosAuth() throws Exception {
final String adUrl = randomFrom("ldap://54.213.145.20:3268", "ldaps://54.213.145.20:3269", AD_LDAP_URL);
final String adUrl = randomFrom(AD_LDAP_URL, AD_LDAP_GC_URL);
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(adUrl, AD_DOMAIN, false), globalSettings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
try (ActiveDirectorySessionFactory sessionFactory = getActiveDirectorySessionFactory(config, sslService, threadPool)) {
@ -290,13 +288,16 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
public void testStandardLdapConnection() throws Exception {
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
Settings settings = LdapTestCase.buildLdapSettings(
new String[] { AD_LDAP_URL },
new String[] { userTemplate },
groupSearchBase,
LdapSearchScope.SUB_TREE,
null,
true);
Settings settings = Settings.builder()
.put(LdapTestCase.buildLdapSettings(
new String[] { AD_LDAP_URL },
new String[] { userTemplate },
groupSearchBase,
LdapSearchScope.SUB_TREE,
null,
true))
.put("follow_referrals", FOLLOW_REFERRALS)
.build();
if (useGlobalSSL == false) {
settings = Settings.builder()
.put(settings)
@ -387,42 +388,6 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
}
}
public void testAdAuthWithHostnameVerification() throws Exception {
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, true), globalSettings,
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings));
try (ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, sslService, threadPool)) {
String userName = "ironman";
UncategorizedExecutionException e = expectThrows(UncategorizedExecutionException.class,
() -> session(sessionFactory, userName, SECURED_PASSWORD));
assertThat(e.getCause(), instanceOf(ExecutionException.class));
assertThat(e.getCause().getCause(), instanceOf(LDAPException.class));
final LDAPException expected = (LDAPException) e.getCause().getCause();
assertThat(expected.getMessage(),
anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated")));
}
}
public void testStandardLdapHostnameVerification() throws Exception {
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
String userTemplate = "CN={0},CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
Settings settings = Settings.builder()
.put(LdapTestCase.buildLdapSettings(AD_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
.put("ssl.verification_mode", VerificationMode.FULL)
.build();
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings),
new ThreadContext(globalSettings));
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, sslService, threadPool);
String user = "Bruce Banner";
UncategorizedExecutionException e = expectThrows(UncategorizedExecutionException.class,
() -> session(sessionFactory, user, SECURED_PASSWORD));
assertThat(e.getCause(), instanceOf(ExecutionException.class));
assertThat(e.getCause().getCause(), instanceOf(LDAPException.class));
final LDAPException expected = (LDAPException) e.getCause().getCause();
assertThat(expected.getMessage(), anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated")));
}
public void testADLookup() throws Exception {
RealmConfig config = new RealmConfig("ad-test",
buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false, true),
@ -450,7 +415,12 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
private Settings buildAdSettings(String ldapUrl, String adDomainName, boolean hostnameVerification, boolean useBindUser) {
Settings.Builder builder = Settings.builder()
.put(SessionFactorySettings.URLS_SETTING, ldapUrl)
.put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, adDomainName);
.put(ActiveDirectorySessionFactorySettings.AD_DOMAIN_NAME_SETTING, adDomainName)
.put(ActiveDirectorySessionFactorySettings.AD_LDAP_PORT_SETTING.getKey(), AD_LDAP_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_LDAPS_PORT_SETTING.getKey(), AD_LDAPS_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_GC_LDAP_PORT_SETTING.getKey(), AD_GC_LDAP_PORT)
.put(ActiveDirectorySessionFactorySettings.AD_GC_LDAPS_PORT_SETTING.getKey(), AD_GC_LDAPS_PORT)
.put("follow_referrals", FOLLOW_REFERRALS);
if (randomBoolean()) {
builder.put("ssl.verification_mode", hostnameVerification ? VerificationMode.FULL : VerificationMode.CERTIFICATE);
} else {

View File

@ -3,9 +3,7 @@
* 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.integration.ldap;
import org.elasticsearch.test.junit.annotations.Network;
package org.elasticsearch.xpack.security.authc.ldap;
import java.io.IOException;
@ -13,8 +11,7 @@ import java.io.IOException;
* This tests the group to role mappings from LDAP sources provided by the super class - available from super.realmConfig.
* The super class will provide appropriate group mappings via configGroupMappings()
*/
@Network
public class GroupMappingTests extends AbstractAdLdapRealmTestCase {
public class GroupMappingIT extends AbstractAdLdapRealmTestCase {
public void testAuthcAuthz() throws IOException {
String avenger = realmConfig.loginWithCommonName ? "Natasha Romanoff" : "blackwidow";
assertAccessAllowed(avenger, "avengers");

View File

@ -3,9 +3,8 @@
* 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.integration.ldap;
package org.elasticsearch.xpack.security.authc.ldap;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.BeforeClass;
import java.io.IOException;
@ -14,8 +13,7 @@ import java.util.ArrayList;
/**
* This tests the mapping of multiple groups to a role in a file based role-mapping
*/
@Network
public class MultiGroupMappingTests extends AbstractAdLdapRealmTestCase {
public class MultiGroupMappingIT extends AbstractAdLdapRealmTestCase {
@BeforeClass
public static void setRoleMappingType() {

View File

@ -3,11 +3,10 @@
* 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.integration.ldap;
package org.elasticsearch.xpack.security.authc.ldap;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.junit.annotations.Network;
import org.junit.BeforeClass;
import java.io.IOException;
@ -21,8 +20,7 @@ import java.util.stream.Collectors;
* The required behaviour is that users from both realms (directory servers) can be authenticated using
* just their userid (the AuthenticationService tries them in order)
*/
@Network
public class MultipleAdRealmTests extends AbstractAdLdapRealmTestCase {
public class MultipleAdRealmIT extends AbstractAdLdapRealmTestCase {
private static RealmConfig secondaryRealmConfig;

View File

@ -10,7 +10,6 @@ import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchScope;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.core.security.support.NoOpLogger;
import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils;
@ -22,7 +21,6 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
@Network
public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
public static final String BRUCE_BANNER_DN = "cn=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";

20
test/smb-fixture/Vagrantfile vendored Normal file
View File

@ -0,0 +1,20 @@
Vagrant.configure("2") do |config|
config.vm.define "test.ad.elastic.local" do |config|
config.vm.box = "elastic/ubuntu-16.04-x86_64"
end
config.vm.hostname = "ad.test.elastic.local"
if Vagrant.has_plugin?("vagrant-cachier")
config.cache.scope = :box
end
config.vm.network "forwarded_port", guest: 389, host: 61389, protocol: "tcp"
config.vm.network "forwarded_port", guest: 636, host: 61636, protocol: "tcp"
config.vm.network "forwarded_port", guest: 3268, host: 63268, protocol: "tcp"
config.vm.network "forwarded_port", guest: 3269, host: 63269, protocol: "tcp"
config.vm.provision "shell", path: "src/main/resources/provision/installsmb.sh"
end

View File

@ -0,0 +1,43 @@
apply plugin: 'elasticsearch.build'
Map<String, String> vagrantEnvVars = [
'VAGRANT_CWD' : "${project.projectDir.absolutePath}",
'VAGRANT_VAGRANTFILE' : 'Vagrantfile',
'VAGRANT_PROJECT_DIR' : "${project.projectDir.absolutePath}"
]
String box = "test.ad.elastic.local"
task update(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'box'
subcommand 'update'
boxName box
environmentVars vagrantEnvVars
}
task up(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'up'
args '--provision', '--provider', 'virtualbox'
boxName box
environmentVars vagrantEnvVars
dependsOn update
}
task halt(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'halt'
boxName box
environmentVars vagrantEnvVars
}
task destroy(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) {
command 'destroy'
args '-f'
boxName box
environmentVars vagrantEnvVars
dependsOn halt
}
thirdPartyAudit.enabled = false
licenseHeaders.enabled = false
test.enabled = false
jarHell.enabled = false

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEApAA/+F2xZ27cKT9m16iyK3KeZclMKeR5wFhIu2OBiH2s8MWm
mP0p2xnv7k/0W/Eu/+3hjDzVgjbhOseTQKcysP9AFpcOI8Z5P1vpP+BZZWbXNCPb
/nruAfZRoiQh9/LurT0Ej0gJdIoMFFdEwuBVHX2KImTBZvdvlNW9pAjgzywSYkgd
d6Lb9x8QpVHmEGmu3N4qS+Dqj0NfH6cO/ZkRRnuK3mkQ3kCMPm3RLfKa2VRTmWqe
vx6vr9Byu5rYRfETUfI8SSk38LCXu0XUgIx2C5++CG3mqXX8+TjdWYEy6Uj/rPl7
7sZnX7Hv1gDXuBpu0Yn6icHTMnGAMGPS22jsfwIDAQABAoIBAAYdrEUK2W7OB4/S
OXeZZuuP3rBVDW4SgyfVIwE5+L6qUSS5ejkCV+k/0l7ExIwZNnN834hnTF8KxON4
RdmHYrCPFEjDYVecMzFVsCEdsLfDWgsruyyGURHpqamuR0YD3TrAp8bgHNonu8OW
bY4G56Wt5NTbhQrd919JiUTwv9F59+6TnP9cubdt2GHDD2M6TkUNpgQS0hnM578X
zrkiQlakAi+rlC2ZQkH94wxKlm53okBliiAykUmbCOGkLUT/GaQLMoN+MZ7Wv6Ib
nsH8lC0KDcH4T9VGmxjlScIJtxGUMO+dNWx6Kg7E/MSwEasUAJOCqIofRtpDUTr4
QJNo4eECgYEA32l+vZLm7OMFxhbqGnueiZXbnc/v0kveeFTDt4OWcUozMBez5H3W
AFFILTRADNbvgEAwuK5oC1hEOH5zoRfnaGXcmWayurD8ibK/t23gE5Xf6rL/vCBN
LMS6WoKXXgCKOwQ0Ke5AoaPmca0Iq8bHFmb4pBF9C/0Z1mc1fc+RWxUCgYEAu+xD
w0zhh5Ktob8Q8eNiqVMrSa9jq0MUS1ljx6qCeIGxbuvQARkJxqms3SXwR4JjEwf3
BAzetYCTFvkqrne9jhoVyZGGS0gLXSG9v3iOaP6GIa51GZwtYhBrzDuGao+UL/Cc
ke4hXpC9S7TSoprW8WWevXVa4dy1kaoFUbrTPkMCgYAxRrx8pcUnZJ9mZLF36+I4
6IPLGA0GblOAaPnOJUjubfZCWkgEUrj70vG/frHN4y5qND5KzbUHI43QhBuO4Y3Z
2fXBJASx5s2ctX9RvvtYdosv4hFD9j/vaujLg9hNFINopvG2eeVpgZQXaJnsAWjy
CP44ed8B4O5s+tCykjC2TQKBgCMxJqt/TUjnRg7hShoSXBqbkaK17rNW14kYz1/H
5bENkJ3WGVjrSHJkuhOcFDhACa+5sR+YDWjuEB2gQcb0c5IV/niGASE996rUM8WU
nQ66g4HxOsq1/aW8r4NKrmxsQPMNWzTU5HjiICD6VuvOlWwVfLm8LW3YuEP0FBTv
KLojAoGAW2EKM7SnstY4khKnC+029aZNuSy/VE6LDcn+E5vwEUgpBN5UTqOWweTv
krlEbD1uAI6aI0Ybc4jM6uyo5LSBzw1TZRS5u3prLZxyyG10JvRD0/f/QTOI21TS
LubgfTc+LXbvUpv6F29lIxHZcIe9lX7cUzHK3Wwo24QOCsXYeqU=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDmzCCAoOgAwIBAgIUdwsnIxjgSneHNVKT6JNCCsrQ3T0wDQYJKoZIhvcNAQEL
BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
cmF0ZWQgQ0EwHhcNMTgwMjE1MTc0ODQ2WhcNMjEwMjE0MTc0ODQ2WjA0MTIwMAYD
VQQDEylFbGFzdGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQAP/hdsWdu3Ck/Zteosity
nmXJTCnkecBYSLtjgYh9rPDFppj9KdsZ7+5P9FvxLv/t4Yw81YI24TrHk0CnMrD/
QBaXDiPGeT9b6T/gWWVm1zQj2/567gH2UaIkIffy7q09BI9ICXSKDBRXRMLgVR19
iiJkwWb3b5TVvaQI4M8sEmJIHXei2/cfEKVR5hBprtzeKkvg6o9DXx+nDv2ZEUZ7
it5pEN5AjD5t0S3ymtlUU5lqnr8er6/Qcrua2EXxE1HyPEkpN/Cwl7tF1ICMdguf
vght5ql1/Pk43VmBMulI/6z5e+7GZ1+x79YA17gabtGJ+onB0zJxgDBj0tto7H8C
AwEAAaOBpDCBoTAdBgNVHQ4EFgQUZo2Y3maL2NoxbbkwRZiC37k6QMEwbwYDVR0j
BGgwZoAUZo2Y3maL2NoxbbkwRZiC37k6QMGhOKQ2MDQxMjAwBgNVBAMTKUVsYXN0
aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2VuZXJhdGVkIENBghR3CycjGOBKd4c1
UpPok0IKytDdPTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf
mkc4bvUR5+We/2rRqCmP4LFnl/LxfbZ9/pUPRdcxuowuK7YfxN8i44VXGpJvLtec
izhA8gvlj6GbYB/GNlHMogqEORbrMlu2o5Cev4HE/pcWpoqtVaDJqI5Hq4763EmJ
p2dXGMmU04H4LtkcCEt3xQfLQ+QIP4Dl2yEsNd248BKSsscCGm9V3vgzFzbdgndo
zUWv9hQCaEsKNtqvnkTqDy2uFjnf+xNoXFr/bI94gvD9HlZHnIC+g0TL5jjtSfCH
gjeXhC2bBKFtlSt4ClIdZTXWievYs6YDRREfaOi4F0757A/gf+hT0fjZ+9WWnUeM
UuvUnl71CNRnJ5JlNKBA
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIUMVGoHuyNTjTFaoRmqFELz75jzDEwDQYJKoZIhvcNAQEL
BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
cmF0ZWQgQ0EwHhcNMTgwMjE1MTc0OTExWhcNMjEwMjE0MTc0OTExWjARMQ8wDQYD
VQQDEwZzYW1iYTQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtGBwa
n+7JN2vweSUsYh4zPmh8RPIE+nEVjK1lx/rADUBY7UVjfTYC+MVKKiezZe7gYCNT
7JNKazPpgVI9e3ZFKw/UxomLqRuuvn5bTh+1tMs3afY5+GGzi7oPmEbBO3ceg0Hi
rNSTDa1rfroZnRYK8uIeSZacQnAW90plITI7rBBt9jq+W9albFbDybfDgNv+yS/C
rzIsofm4rbFC3SMRYfrT6HvwDhjOmmYKZci5x7tsn0T+3tSiR44Bw5/DgiN5kX3m
/kl9qg1eoYWbCUy1dKmQlb4Nb4uNcxrIugLB3zjBkfhMZ0OHoveKh/lJASTWik9k
xQ9rEYbpsRbuXpsHAgMBAAGjgcwwgckwHQYDVR0OBBYEFJOLa7UXKtLPibgKeFh7
Kq1+rS0/MG8GA1UdIwRoMGaAFGaNmN5mi9jaMW25MEWYgt+5OkDBoTikNjA0MTIw
MAYDVQQDEylFbGFzdGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBD
QYIUdwsnIxjgSneHNVKT6JNCCsrQ3T0wLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/
AAABhxAAAAAAAAAAAAAAAAAAAAABMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQAD
ggEBAEHqT1WHkcF8DuOgyIBx7wKcUVQ5H1qYYlJ1xgMGrKFFZLUzouLcON7oadEu
HLIJ4Z3AKD3bqWpcls5XJ9MTECGR48tou67x9cXqTV7jR3Rh0H/VGwzwhR85vbpu
o8ielOPL8XAQOfnAFESJii5sfCU4ZwLg+3evmGZdKfhU6rqQtLimgG/Gm96vOJne
y0a/TZTWrfAarithkOHHXSSAhEI5SdW5SlZAytF4AmYqFvafwxe1+NyFwfCRy0Xl
H40WgVsq+z84psU+WyORb3THX5rgB4au9nuMXOqFKAtrJSI/uApncYraaqU28rqB
gYd8XrtjhKOLw+6viqAKu8l7/cs=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArRgcGp/uyTdr8HklLGIeMz5ofETyBPpxFYytZcf6wA1AWO1F
Y302AvjFSions2Xu4GAjU+yTSmsz6YFSPXt2RSsP1MaJi6kbrr5+W04ftbTLN2n2
Ofhhs4u6D5hGwTt3HoNB4qzUkw2ta366GZ0WCvLiHkmWnEJwFvdKZSEyO6wQbfY6
vlvWpWxWw8m3w4Db/skvwq8yLKH5uK2xQt0jEWH60+h78A4YzppmCmXIuce7bJ9E
/t7UokeOAcOfw4IjeZF95v5JfaoNXqGFmwlMtXSpkJW+DW+LjXMayLoCwd84wZH4
TGdDh6L3iof5SQEk1opPZMUPaxGG6bEW7l6bBwIDAQABAoIBAFkjr2Vus3PgHLAs
Ux52MQNGwlwszU4PAymL1sgxokpBCMBDAJbppmUFY+R7rRJQDiJyn/7aOEf8yTEZ
LhcHe7LHKFH1JGRN5DmrVDsFEoNq5bRV1z2nUfk6ncjmLJnaW8/U3Js1Ugug4YwY
KRKDuRROXHAoiW1TMZJCK4fE/q+HZeG/lz110C+GJfhtCH6PzowC4eVPeh1/FExl
TFGRFh2qnN/d5IfGWaZwazTR16OGOoZf/WYydBilcugxQFNx5osR+4nAFdMW0xD2
x2diukMf+WBjIWlO3vt9GMs8PBQsU2Ix3/+MUxC5MCUafL1GBqUSphB4YWTrNWQ0
izFEYZECgYEA1Ue8S0xL6Qxy8F1cZj+tvoGlblCTxHL65JeMl6LwMHzuNHX27s8Q
Ax9j8Z2MTWH7IBATmA56CYlA09FWRiIr38k7cNUC8KdjvjPeqHKJeTIhluiBWGLl
AE2XbEkOdjBXXg7ipF0tcBWb+/WrobzJ2T9dpMzZZOnWEB+7wsN2fy0CgYEAz8PG
TJ0u/+2q+RfeAxo0zdC/dwx8T5rJfZD0AiJpEu69oqMZdiX2M1JDabh/CFdYWyAm
AWQdSw0ugeUHeiZ0i2gujtbxDLAhpCsQ1jleJpJm7VWvgCKY7FotmXhVxA8Acmm0
slv280ezNJMJFKIONuuOtDATdX1b+MXoh5D2A4MCgYAuadwKLuJeJv1kXYzcG4N9
78zGgvaFS9hZorlPzn+ira1Q8VL5iUocw9oGHJkJxgbWZWk+L/hS1vGqpuW1gX42
xx4OYeyv3l2QaM1Nrw9Htqckphhv2aWoOTp4sDVbdw2sRGUCC9z1hV5aqI3fNqxe
gLGqSYINue2BuMYtjkfdSQKBgC0uefU3SX1GhiPdWN572HfZqYmOIYp+Mssntqiw
KwF/AaZYqbTT1JKclSRshtOdiw1mFF3BE826dB6zW8joi/e1FErj20/TDb3Rz7uG
hj8FH3UFaUEIRRFBGyGA1cXpLUO0USNodG+7a/FG+HaQN18iIsp0mga22EVlZIf2
sklZAoGAOJTQtMJfVk5KvqaZWnFou1De5BLnTq7LEvuNHlddIlXG0QQe9QcyB82G
UFqbpBR8QBTFsHdIQEA4LNQE7L1WrKR2Qj087QXHaUnlo7x2WPoSyziPet0nUn3E
gE5dgnkzX/MENKjG90wJFWNiJqz3JXIbVZKZLzkbM+u+X2+oRvs=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,95 @@
#! /bin/bash
# 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.
set -ex
MARKER_FILE=/etc/marker
if [ -f $MARKER_FILE ]; then
echo "Already provisioned..."
exit 0;
fi
VDIR=/vagrant
RESOURCES=$VDIR/src/main/resources
CERTS_DIR=$RESOURCES/certs
SSL_DIR=/var/lib/samba/private/tls
# Update package manager
apt-get update -qqy
# Install krb5 packages
apt-get install -qqy samba ldap-utils
# install ssl certs
mkdir -p $SSL_DIR
cp $CERTS_DIR/*.pem $SSL_DIR
chmod 600 $SSL_DIR/key.pem
cat $SSL_DIR/ca.pem >> /etc/ssl/certs/ca-certificates.crt
mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
samba-tool domain provision --server-role=dc --use-rfc2307 --dns-backend=SAMBA_INTERNAL --realm=AD.TEST.ELASTICSEARCH.COM --domain=ADES --adminpass=Passw0rd
cp /var/lib/samba/private/krb5.conf /etc/krb5.conf
service samba-ad-dc restart
# Add users
samba-tool user add ironman Passw0rd --surname=Stark --given-name=Tony --job-title=CEO
samba-tool user add hulk Passw0rd --surname=Banner --given-name=Bruce
samba-tool user add phil Passw0rd --surname=Coulson --given-name=Phil
samba-tool user add cap Passw0rd --surname=Rogers --given-name=Steve
samba-tool user add blackwidow Passw0rd --surname=Romanoff --given-name=Natasha
samba-tool user add hawkeye Passw0rd --surname=Barton --given-name=Clint
samba-tool user add Thor Passw0rd
samba-tool user add selvig Passw0rd --surname=Selvig --given-name=Erik
samba-tool user add Odin Passw0rd
samba-tool user add Jarvis Passw0rd
samba-tool user add kraken Passw0rd --surname=Kraken --given-name=Commander
samba-tool user add fury Passw0rd --surname=Fury --given-name=Nick
# Add groups
samba-tool group add SHIELD
samba-tool group add Avengers
samba-tool group add Supers
samba-tool group add Geniuses
samba-tool group add Playboys
samba-tool group add Philanthropists
samba-tool group add Gods
samba-tool group add Billionaires
samba-tool group add "World Security Council"
samba-tool group add Hydra
# Group membership
samba-tool group addmembers "SHIELD" Thor,hawkeye,blackwidow,cap,phil,hulk,ironman
samba-tool group addmembers "Avengers" Thor,hawkeye,blackwidow,cap,hulk,ironman
samba-tool group addmembers "Supers" Avengers
samba-tool group addmembers "Geniuses" selvig,hulk,ironman
samba-tool group addmembers "Playboys" ironman
samba-tool group addmembers "Philanthropists" Thor,hulk,ironman
samba-tool group addmembers "Gods" Thor,Odin
samba-tool group addmembers "Billionaires" ironman
samba-tool group addmembers "World Security Council" fury
samba-tool group addmembers "Hydra" kraken
# update UPN
cat > /tmp/entrymods << EOL
dn: CN=Erik Selvig,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com
changetype: modify
replace: userPrincipalName
userPrincipalName: erik.selvig@ad.test.elasticsearch.com
dn: CN=Bruce Banner,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com
changetype: modify
add: seeAlso
seeAlso: CN=Avengers,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com
EOL
ldapmodify -D Administrator@ad.test.elasticsearch.com -w Passw0rd -H ldaps://127.0.0.1:636 -f /tmp/entrymods -v
touch $MARKER_FILE