blob: 828ded7c5625d7fbfdbcbdc47fc0de961934c0f3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.exec.rpc.user.security;
import com.google.common.collect.Lists;
import com.typesafe.config.ConfigValueFactory;
import org.apache.drill.categories.SecurityTest;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.config.DrillProperties;
import org.apache.drill.exec.ExecConstants;
import org.apache.drill.exec.rpc.NonTransientRpcException;
import org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl;
import org.apache.drill.test.BaseTestQuery;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.security.sasl.SaslException;
import java.util.Properties;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
/**
* Helps to test different scenarios based on security configuration on client and Drillbit side with respect to SASL
* and specifically using PLAIN mechanism
*/
@Category({SecurityTest.class})
public class TestUserBitSaslCompatibility extends BaseTestQuery {
@BeforeClass
public static void setup() {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
updateTestCluster(1, newConfig, connectionProps);
}
/**
* Test showing when Drillbit is not configured for authentication whereas client explicitly requested for PLAIN
* authentication then connection succeeds without authentication.
* @throws Exception
*/
@Test
public void testDisableDrillbitAuth_EnableClientAuthPlain() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
try {
updateTestCluster(1, newConfig, connectionProps);
} catch (Exception ex) {
fail();
}
}
/**
* Test showing when Drillbit is not configured for authentication whereas client explicitly requested for Kerberos
* authentication then connection fails due to new check before SASL Handshake.
* @throws Exception
*/
@Test
public void testDisableDrillbitAuth_EnableClientAuthKerberos() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.AUTH_MECHANISM, "kerberos");
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(!(ex.getCause().getCause() instanceof SaslException));
}
}
/**
* Test showing failure before SASL handshake when Drillbit is not configured for authentication whereas client
* explicitly requested for encrypted connection.
* @throws Exception
*/
@Test
public void testDisableDrillbitAuth_EnableClientEncryption() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
connectionProps.setProperty(DrillProperties.SASL_ENCRYPT, "true");
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(!(ex.getCause().getCause() instanceof SaslException));
}
}
/**
* Test showing failure before SASL handshake when Drillbit is not configured for encryption whereas client explicitly
* requested for encrypted connection.
* @throws Exception
*/
@Test
public void testDisableDrillbitEncryption_EnableClientEncryption() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
connectionProps.setProperty(DrillProperties.SASL_ENCRYPT, "true");
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(!(ex.getCause().getCause() instanceof SaslException));
}
}
/**
* Test showing failure in SASL handshake when Drillbit is configured for authentication only whereas client doesn't
* provide any security properties like username/password in this case.
* @throws Exception
*/
@Test
public void testEnableDrillbitAuth_DisableClientAuth() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(ex.getCause().getCause() instanceof SaslException);
}
}
/**
* Test showing failure in SASL handshake when Drillbit is configured for encryption whereas client doesn't provide any
* security properties like username/password in this case.
* @throws Exception
*/
@Test
public void testEnableDrillbitEncryption_DisableClientAuth() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(true)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(ex.getCause().getCause() instanceof SaslException);
}
}
/**
* Test showing successful SASL handshake when both Drillbit and client side authentication is enabled using PLAIN
* mechanism.
* @throws Exception
*/
@Test
public void testEnableDrillbitClientAuth() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
try {
updateTestCluster(1, newConfig, connectionProps);
} catch (Exception ex) {
fail();
}
}
/**
* Below test shows the failure in Sasl layer with client and Drillbit side encryption enabled using PLAIN
* mechanism. This is expected since PLAIN mechanism doesn't support encryption using SASL. Whereas same test
* setup using Kerberos or any other mechanism with encryption support will result in successful SASL handshake.
* @throws Exception
*/
@Test
public void testEnableDrillbitClientEncryption_UsingPlain() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.USER_AUTHENTICATOR_IMPL,
ConfigValueFactory.fromAnyRef(UserAuthenticatorTestImpl.TYPE))
.withValue(ExecConstants.AUTHENTICATION_MECHANISMS,
ConfigValueFactory.fromIterable(Lists.newArrayList("plain")))
.withValue(ExecConstants.USER_ENCRYPTION_SASL_ENABLED,
ConfigValueFactory.fromAnyRef(true)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
connectionProps.setProperty(DrillProperties.PASSWORD, "anything works!");
connectionProps.setProperty(DrillProperties.SASL_ENCRYPT, "true");
try {
updateTestCluster(1, newConfig, connectionProps);
fail();
} catch (Exception ex) {
assertTrue(ex.getCause() instanceof NonTransientRpcException);
assertTrue(ex.getCause().getCause() instanceof SaslException);
}
}
/**
* Test showing successful handshake when authentication is disabled on Drillbit side and client also
* doesn't provide any security properties in connection URL.
* @throws Exception
*/
@Test
public void testDisableDrillbitClientAuth() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false)));
final Properties connectionProps = new Properties();
try {
updateTestCluster(1, newConfig, connectionProps);
} catch (Exception ex) {
fail();
}
}
/**
* Test showing successful handshake when authentication is disabled but impersonation is enabled on Drillbit side
* and client only provides USERNAME as a security property in connection URL.
* @throws Exception
*/
@Test
public void testEnableDrillbitImpersonation_DisableClientAuth() throws Exception {
final DrillConfig newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_AUTHENTICATION_ENABLED,
ConfigValueFactory.fromAnyRef(false))
.withValue(ExecConstants.IMPERSONATION_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.IMPERSONATION_MAX_CHAINED_USER_HOPS,
ConfigValueFactory.fromAnyRef(3)));
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.USER, "anonymous");
try {
updateTestCluster(1, newConfig, connectionProps);
} catch (Exception ex) {
fail();
}
}
}