org.apache.hadoop.hbase.security.AbstractTestSecureIPC.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.security.AbstractTestSecureIPC.java

Source

/**
 *
 * 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.hadoop.hbase.security;

import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getKeytabFileForTesting;
import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getPrincipalForTesting;
import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getSecuredConfiguration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ThreadLocalRandom;

import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellScanner;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.ipc.FifoRpcScheduler;
import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
import org.apache.hadoop.hbase.ipc.RpcClient;
import org.apache.hadoop.hbase.ipc.RpcClientFactory;
import org.apache.hadoop.hbase.ipc.RpcServer;
import org.apache.hadoop.hbase.ipc.RpcServerInterface;
import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos;
import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos;
import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;

import com.google.common.collect.Lists;
import com.google.protobuf.BlockingRpcChannel;
import com.google.protobuf.BlockingService;

import javax.security.sasl.SaslException;

public abstract class AbstractTestSecureIPC {

    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();

    private static final File KEYTAB_FILE = new File(TEST_UTIL.getDataTestDir("keytab").toUri().getPath());

    static final BlockingService SERVICE = TestRpcServiceProtos.TestProtobufRpcProto
            .newReflectiveBlockingService(new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {

                @Override
                public TestProtos.EmptyResponseProto ping(RpcController controller,
                        TestProtos.EmptyRequestProto request) throws ServiceException {
                    return null;
                }

                @Override
                public TestProtos.EmptyResponseProto error(RpcController controller,
                        TestProtos.EmptyRequestProto request) throws ServiceException {
                    return null;
                }

                @Override
                public TestProtos.EchoResponseProto echo(RpcController controller,
                        TestProtos.EchoRequestProto request) throws ServiceException {
                    if (controller instanceof PayloadCarryingRpcController) {
                        PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController) controller;
                        // If cells, scan them to check we are able to iterate what we were given and since
                        // this is
                        // an echo, just put them back on the controller creating a new block. Tests our
                        // block
                        // building.
                        CellScanner cellScanner = pcrc.cellScanner();
                        List<Cell> list = null;
                        if (cellScanner != null) {
                            list = new ArrayList<Cell>();
                            try {
                                while (cellScanner.advance()) {
                                    list.add(cellScanner.current());
                                }
                            } catch (IOException e) {
                                throw new ServiceException(e);
                            }
                        }
                        cellScanner = CellUtil.createCellScanner(list);
                        ((PayloadCarryingRpcController) controller).setCellScanner(cellScanner);
                    }
                    return TestProtos.EchoResponseProto.newBuilder().setMessage(request.getMessage()).build();
                }
            });

    private static MiniKdc KDC;
    private static String HOST = "localhost";
    private static String PRINCIPAL;

    String krbKeytab;
    String krbPrincipal;
    UserGroupInformation ugi;
    Configuration clientConf;
    Configuration serverConf;

    abstract Class<? extends RpcClient> getRpcClientClass();

    @Rule
    public ExpectedException exception = ExpectedException.none();

    @BeforeClass
    public static void setUp() throws Exception {
        Properties conf = MiniKdc.createConf();
        conf.put(MiniKdc.DEBUG, true);
        KDC = new MiniKdc(conf, new File(TEST_UTIL.getDataTestDir("kdc").toUri().getPath()));
        KDC.start();
        PRINCIPAL = "hbase/" + HOST;
        KDC.createPrincipal(KEYTAB_FILE, PRINCIPAL);
        HBaseKerberosUtils.setKeytabFileForTesting(KEYTAB_FILE.getAbsolutePath());
        HBaseKerberosUtils.setPrincipalForTesting(PRINCIPAL + "@" + KDC.getRealm());
    }

    @AfterClass
    public static void tearDown() throws IOException {
        if (KDC != null) {
            KDC.stop();
        }
        TEST_UTIL.cleanupTestDir();
    }

    @Before
    public void setUpTest() throws Exception {
        krbKeytab = getKeytabFileForTesting();
        krbPrincipal = getPrincipalForTesting();
        ugi = loginKerberosPrincipal(krbKeytab, krbPrincipal);
        clientConf = getSecuredConfiguration();
        clientConf.set(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, getRpcClientClass().getName());
        serverConf = getSecuredConfiguration();
    }

    @Test
    public void testRpcCallWithEnabledKerberosSaslAuth() throws Exception {
        UserGroupInformation ugi2 = UserGroupInformation.getCurrentUser();

        // check that the login user is okay:
        assertSame(ugi, ugi2);
        assertEquals(AuthenticationMethod.KERBEROS, ugi.getAuthenticationMethod());
        assertEquals(krbPrincipal, ugi.getUserName());

        callRpcService(User.create(ugi2));
    }

    @Test
    public void testRpcFallbackToSimpleAuth() throws Exception {
        String clientUsername = "testuser";
        UserGroupInformation clientUgi = UserGroupInformation.createUserForTesting(clientUsername,
                new String[] { clientUsername });

        // check that the client user is insecure
        assertNotSame(ugi, clientUgi);
        assertEquals(AuthenticationMethod.SIMPLE, clientUgi.getAuthenticationMethod());
        assertEquals(clientUsername, clientUgi.getUserName());

        clientConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
        serverConf.setBoolean(RpcServer.FALLBACK_TO_INSECURE_CLIENT_AUTH, true);
        callRpcService(User.create(clientUgi));
    }

    void setRpcProtection(String clientProtection, String serverProtection) {
        clientConf.set("hbase.rpc.protection", clientProtection);
        serverConf.set("hbase.rpc.protection", serverProtection);
    }

    /**
     * Test various combinations of Server and Client qops.
     * @throws Exception
     */
    @Test
    public void testSaslWithCommonQop() throws Exception {
        setRpcProtection("privacy,authentication", "authentication");
        callRpcService(User.create(ugi));

        setRpcProtection("authentication", "privacy,authentication");
        callRpcService(User.create(ugi));

        setRpcProtection("integrity,authentication", "privacy,authentication");
        callRpcService(User.create(ugi));

        setRpcProtection("integrity,authentication", "integrity,authentication");
        callRpcService(User.create(ugi));

        setRpcProtection("privacy,authentication", "privacy,authentication");
        callRpcService(User.create(ugi));
    }

    @Test
    public void testSaslNoCommonQop() throws Exception {
        exception.expect(SaslException.class);
        exception.expectMessage("No common protection layer between client and server");
        setRpcProtection("integrity", "privacy");
        callRpcService(User.create(ugi));
    }

    private UserGroupInformation loginKerberosPrincipal(String krbKeytab, String krbPrincipal) throws Exception {
        Configuration cnf = new Configuration();
        cnf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
        UserGroupInformation.setConfiguration(cnf);
        UserGroupInformation.loginUserFromKeytab(krbPrincipal, krbKeytab);
        return UserGroupInformation.getLoginUser();
    }

    /**
     * Sets up a RPC Server and a Client. Does a RPC checks the result. If an exception is thrown
     * from the stub, this function will throw root cause of that exception.
     */
    private void callRpcService(User clientUser) throws Exception {
        SecurityInfo securityInfoMock = Mockito.mock(SecurityInfo.class);
        Mockito.when(securityInfoMock.getServerPrincipal()).thenReturn(HBaseKerberosUtils.KRB_PRINCIPAL);
        SecurityInfo.addInfo("TestProtobufRpcProto", securityInfoMock);

        InetSocketAddress isa = new InetSocketAddress(HOST, 0);

        RpcServerInterface rpcServer = new RpcServer(null, "AbstractTestSecureIPC",
                Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(SERVICE, null)), isa, serverConf,
                new FifoRpcScheduler(serverConf, 1));
        rpcServer.start();
        try (RpcClient rpcClient = RpcClientFactory.createClient(clientConf,
                HConstants.DEFAULT_CLUSTER_ID.toString())) {
            InetSocketAddress address = rpcServer.getListenerAddress();
            if (address == null) {
                throw new IOException("Listener channel is closed");
            }
            BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel(
                    ServerName.valueOf(address.getHostName(), address.getPort(), System.currentTimeMillis()),
                    clientUser, 0);
            TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub = TestRpcServiceProtos.TestProtobufRpcProto
                    .newBlockingStub(channel);
            List<String> results = new ArrayList<>();
            TestThread th1 = new TestThread(stub, results);
            final Throwable exception[] = new Throwable[1];
            Collections.synchronizedList(new ArrayList<Throwable>());
            Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
                public void uncaughtException(Thread th, Throwable ex) {
                    exception[0] = ex;
                }
            };
            th1.setUncaughtExceptionHandler(exceptionHandler);
            th1.start();
            th1.join();
            if (exception[0] != null) {
                // throw root cause.
                while (exception[0].getCause() != null) {
                    exception[0] = exception[0].getCause();
                }
                throw (Exception) exception[0];
            }
        } finally {
            rpcServer.stop();
        }
    }

    public static class TestThread extends Thread {
        private final TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub;

        private final List<String> results;

        public TestThread(TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub, List<String> results) {
            this.stub = stub;
            this.results = results;
        }

        @Override
        public void run() {
            try {
                int[] messageSize = new int[] { 100, 1000, 10000 };
                for (int i = 0; i < messageSize.length; i++) {
                    String input = RandomStringUtils.random(messageSize[i]);
                    String result = stub
                            .echo(null, TestProtos.EchoRequestProto.newBuilder().setMessage(input).build())
                            .getMessage();
                    assertEquals(input, result);
                }
            } catch (ServiceException e) {
                throw new RuntimeException(e);
            }
        }
    }
}