org.apache.hadoop.hbase.test.IntegrationTestZKAndFSPermissions.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.test.IntegrationTestZKAndFSPermissions.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.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.IntegrationTestingUtility;
import org.apache.hadoop.hbase.testclassification.IntegrationTests;
import org.apache.hadoop.hbase.util.AbstractHBaseTool;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hbase.zookeeper.RecoverableZooKeeper;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import org.apache.hadoop.util.ToolRunner;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooDefs.Perms;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import org.junit.experimental.categories.Category;

/**
 * An integration test which checks that the znodes in zookeeper and data in the FileSystem
 * are protected for secure HBase deployments.
 * This test is intended to be run on clusters with kerberos authorization for HBase and Zookeeper.
 *
 * If hbase.security.authentication is not set to kerberos, the test does not run unless -f is
 * specified which bypasses the check. It is recommended to always run with -f on secure clusters
 * so that the test checks the actual end result, not the configuration.
 *
 * The test should be run as hbase user with kinit / TGT cached since it accesses HDFS.
 * <p>
 * Example usage:
 *   hbase org.apache.hadoop.hbase.test.IntegrationTestZnodeACLs -h
 */
@Category(IntegrationTests.class)
public class IntegrationTestZKAndFSPermissions extends AbstractHBaseTool {

    private static final Log LOG = LogFactory.getLog(IntegrationTestZKAndFSPermissions.class);
    private String superUser;
    private String masterPrincipal;
    private boolean isForce;
    private String fsPerms;
    private boolean skipFSCheck;
    private boolean skipZKCheck;

    public static final String FORCE_CHECK_ARG = "f";
    public static final String PRINCIPAL_ARG = "p";
    public static final String SUPERUSER_ARG = "s";
    public static final String FS_PERMS = "fs_perms";
    public static final String SKIP_CHECK_FS = "skip_fs_check";
    public static final String SKIP_CHECK_ZK = "skip_zk_check";

    @Override
    public void setConf(Configuration conf) {
        super.setConf(conf);
    }

    @Override
    protected void addOptions() {
        addOptNoArg(FORCE_CHECK_ARG, "Whether to skip configuration lookup and assume a secure setup");
        addOptWithArg(PRINCIPAL_ARG, "The principal for zk authorization");
        addOptWithArg(SUPERUSER_ARG, "The principal for super user");
        addOptWithArg(FS_PERMS, "FS permissions, ex. 700, 750, etc. Defaults to 700");
        addOptNoArg(SKIP_CHECK_FS, "Whether to skip checking FS permissions");
        addOptNoArg(SKIP_CHECK_ZK, "Whether to skip checking ZK permissions");
    }

    @Override
    protected void processOptions(CommandLine cmd) {
        isForce = cmd.hasOption(FORCE_CHECK_ARG);
        masterPrincipal = getShortUserName(conf.get("hbase.master.kerberos.principal"));
        superUser = cmd.getOptionValue(SUPERUSER_ARG, conf.get("hbase.superuser"));
        masterPrincipal = cmd.getOptionValue(PRINCIPAL_ARG, masterPrincipal);
        fsPerms = cmd.getOptionValue(FS_PERMS, "700");
        skipFSCheck = cmd.hasOption(SKIP_CHECK_FS);
        skipZKCheck = cmd.hasOption(SKIP_CHECK_ZK);
    }

    private String getShortUserName(String principal) {
        for (int i = 0; i < principal.length(); i++) {
            if (principal.charAt(i) == '/' || principal.charAt(i) == '@') {
                return principal.substring(0, i);
            }
        }
        return principal;
    }

    @Override
    protected int doWork() throws Exception {
        if (!isForce) {
            if (!"kerberos".equalsIgnoreCase(conf.get("hbase.security.authentication"))) {
                LOG.warn("hbase.security.authentication is not kerberos, and -f is not supplied. Skip "
                        + "running the test");
                return 0;
            }
        }

        if (!skipZKCheck) {
            testZNodeACLs();
        }
        if (!skipFSCheck) {
            testFSPerms();
        }
        return 0;
    }

    private void testZNodeACLs() throws IOException, KeeperException, InterruptedException {

        ZooKeeperWatcher watcher = new ZooKeeperWatcher(conf, "IntegrationTestZnodeACLs", null);
        RecoverableZooKeeper zk = ZKUtil.connect(this.conf, watcher);

        String baseZNode = watcher.baseZNode;

        LOG.info("");
        LOG.info("***********************************************************************************");
        LOG.info("Checking ZK permissions, root znode: " + baseZNode);
        LOG.info("***********************************************************************************");
        LOG.info("");

        checkZnodePermsRecursive(watcher, zk, baseZNode);

        LOG.info("Checking ZK permissions: SUCCESS");
    }

    private void checkZnodePermsRecursive(ZooKeeperWatcher watcher, RecoverableZooKeeper zk, String znode)
            throws KeeperException, InterruptedException {

        boolean expectedWorldReadable = watcher.isClientReadable(znode);

        assertZnodePerms(zk, znode, expectedWorldReadable);

        try {
            List<String> children = zk.getChildren(znode, false);

            for (String child : children) {
                checkZnodePermsRecursive(watcher, zk, ZKUtil.joinZNode(znode, child));
            }
        } catch (KeeperException ke) {
            // if we are not authenticated for listChildren, it is fine.
            if (ke.code() != Code.NOAUTH) {
                throw ke;
            }
        }
    }

    private void assertZnodePerms(RecoverableZooKeeper zk, String znode, boolean expectedWorldReadable)
            throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        List<ACL> acls = zk.getZooKeeper().getACL(znode, stat);

        LOG.info("Checking ACLs for znode znode:" + znode + " acls:" + acls);

        for (ACL acl : acls) {
            int perms = acl.getPerms();
            Id id = acl.getId();
            // We should only set at most 3 possible ACL for 3 Ids. One for everyone, one for superuser
            // and one for the hbase user
            if (Ids.ANYONE_ID_UNSAFE.equals(id)) {
                // everyone should be set only if we are expecting this znode to be world readable
                assertTrue(expectedWorldReadable);
                // assert that anyone can only read
                assertEquals(perms, Perms.READ);
            } else if (superUser != null && new Id("sasl", superUser).equals(id)) {
                // assert that super user has all the permissions
                assertEquals(perms, Perms.ALL);
            } else if (new Id("sasl", masterPrincipal).equals(id)) {
                // hbase.master.kerberos.principal?
                assertEquals(perms, Perms.ALL);
            } else {
                fail("An ACL is found which is not expected for the znode:" + znode + " , ACL:" + acl);
            }
        }
    }

    private void testFSPerms() throws IOException {
        Path rootDir = FSUtils.getRootDir(conf);

        LOG.info("");
        LOG.info("***********************************************************************************");
        LOG.info("Checking FS permissions for root dir:" + rootDir);
        LOG.info("***********************************************************************************");
        LOG.info("");
        FileSystem fs = rootDir.getFileSystem(conf);

        short expectedPerms = Short.valueOf(fsPerms, 8);

        assertEquals(FsPermission.createImmutable(expectedPerms), fs.getFileStatus(rootDir).getPermission());

        LOG.info("Checking FS permissions: SUCCESS");
    }

    public static void main(String[] args) throws Exception {
        Configuration configuration = HBaseConfiguration.create();
        IntegrationTestingUtility.setUseDistributedCluster(configuration);
        IntegrationTestZKAndFSPermissions tool = new IntegrationTestZKAndFSPermissions();
        int ret = ToolRunner.run(configuration, tool, args);
        System.exit(ret);
    }
}