HDFS源码剖析-HDFS的鉴权

1 HDFS权限控制和ACL

官网文档:http://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html

中文翻译:https://blog.youkuaiyun.com/shubingzhuoxue/article/details/50072271

acl涉及的参数:

dfs.permissions.enabled

dfs.web.ugi

dfs.permissions.superusergroup

fs.permissions.umask-mode

dfs.cluster.administrators

dfs.namenode.acls.enabled

2 HDFS鉴权的流程

以HDFS Client为例,来分析下HDFS鉴权的过程

hdfs client分析:hdfs dfs -ls一文中,我们已经知道client在创建出FileSystem对象后,就会去调用fs.listStatus方法去列取目录信息。listStatus底层是通过RPC调用到NameNode的listStatus方法,那么ls的鉴权肯定就是在NameNode的listStatus方法中进行的了。

进入DistributedFileSystem.listStatus方法

//DistributedFileSystem.java
@Override
  public FileStatus[] listStatus(Path p) throws IOException {
    Path absF = fixRelativePart(p);
    return new FileSystemLinkResolver<FileStatus[]>() {
      @Override
      public FileStatus[] doCall(final Path p)
          throws IOException, UnresolvedLinkException {
        return listStatusInternal(p);
      }
      @Override
      public FileStatus[] next(final FileSystem fs, final Path p)
          throws IOException {
        return fs.listStatus(p);
      }
    }.resolve(this, absF);
  }

再进入listStatusInternal(p)

//DistributedFileSystem.java
private FileStatus[] listStatusInternal(Path p) throws IOException {
    String src = getPathName(p);

    // fetch the first batch of entries in the directory
    DirectoryListing thisListing = dfs.listPaths(
        src, HdfsFileStatus.EMPTY_NAME);

    if (thisListing == null) { // the directory does not exist
      throw new FileNotFoundException("File " + p + " does not exist.");
    }
    ...
}

再进入dfs.listPaths(src, HdfsFileStatus.EMPTY_NAME)

//DFSClient.java 
public DirectoryListing listPaths(String src,  byte[] startAfter)
    throws IOException {
    return listPaths(src, startAfter, false);
  }

再进入listPaths(src, startAfter, false)

//DFSClient.java
public DirectoryListing listPaths(String src,  byte[] startAfter,
      boolean needLocation) throws IOException {
    checkOpen();
    TraceScope scope = getPathTraceScope("listPaths", src);
    try {
      return namenode.getListing(src, startAfter, needLocation);
    } catch(RemoteException re) {
      throw re.unwrapRemoteException(AccessControlException.class,
                                     FileNotFoundException.class,
                                     UnresolvedPathException.class);
    } finally {
      scope.close();
    }
  }

namenode.getListing最终是调用到了NameNodeRPCServer端了

//NameNodeRpcServer.java
public DirectoryListing getListing(String src, byte[] startAfter,
      boolean needLocation) throws IOException {
    checkNNStartup();
    DirectoryListing files = namesystem.getListing(
        src, startAfter, needLocation);
    if (files != null) {
      metrics.incrGetListingOps();
      metrics.incrFilesInGetListingOps(files.getPartialListing().length);
    }
    return files;
  }

进入namesystem.getListing

DirectoryListing getListing(String src, byte[] startAfter,
      boolean needLocation) 
      throws IOException {
    checkOperation(OperationCategory.READ);
    DirectoryListing dl = null;
    readLock();
    try {
      checkOperation(NameNode.OperationCategory.READ);
      dl = FSDirStatAndListingOp.getListingInt(dir, src, startAfter,
          needLocation);
    } catch (AccessControlException e) {
      logAuditEvent(false, "listStatus", src);
      throw e;
    } finally {
      readUnlock();
    }
    logAuditEvent(true, "listStatus", src);
    return dl;
  }

终于看到AccessControlException出现了,看来getListingInt

static DirectoryListing getListingInt(FSDirectory fsd, final String srcArg,
      byte[] startAfter, boolean needLocation) throws IOException {
    //创建出FSPermissionChecker对象,此对象包含client端的UGI信息
    FSPermissionChecker pc = fsd.getPermissionChecker();
    byte[][] pathComponents = FSDirectory
        .getPathComponentsForReservedPath(srcArg);
    final String startAfterString = new String(startAfter, Charsets.UTF_8);
    final String src = fsd.resolvePath(pc, srcArg, pathComponents);
    final INodesInPath iip = fsd.getINodesInPath(src, true);

    // Get file name when startAfter is an INodePath
    if (FSDirectory.isReservedName(startAfterString)) {
      byte[][] startAfterComponents = FSDirectory
          .getPathComponentsForReservedPath(startAfterString);
      try {
        String tmp = FSDirectory.resolvePath(src, startAfterComponents, fsd);
        byte[][] regularPath = INode.getPathComponents(tmp);
        startAfter = regularPath[regularPath.length - 1];
      } catch (IOException e) {
        // Possibly the inode is deleted
        throw new DirectoryListingStartAfterNotFoundException(
            "Can't find startAfter " + startAfterString);
      }
    }

    boolean isSuperUser = true;
    /**
     * 开启鉴权,dfs.permissions.enabled
     * 进行path的权限验证
     */
    if (fsd.isPermissionEnabled()) {
      if (iip.getLastINode() != null && iip.getLastINode().isDirectory()) {
        fsd.checkPathAccess(pc, iip, FsAction.READ_EXECUTE);
      } else {
        fsd.checkTraverse(pc, iip);
      }
      isSuperUser = pc.isSuperUser();
    }
    return getListing(fsd, iip, src, startAfter, needLocation, isSuperUser);
  }

先来看下fsd.getPermissionChecker()

FSPermissionChecker getPermissionChecker()
  throws AccessControlException {
  try {
    return getPermissionChecker(fsOwnerShortUserName, supergroup,
        NameNode.getRemoteUser());
  } catch (IOException e) {
    throw new AccessControlException(e);
  }
}
public static UserGroupInformation getRemoteUser() throws IOException {
  UserGroupInformation ugi = Server.getRemoteUser();
  return (ugi != null) ? ugi : UserGroupInformation.getCurrentUser();
}
/** Returns the RPC remote user when invoked inside an RPC.  Note this
 *  may be different than the current user if called within another doAs
 *  @return connection's UGI or null if not an RPC
 */
public static UserGroupInformation getRemoteUser() {
  Call call = CurCall.get();
  return (call != null && call.connection != null) ? call.connection.user
      : null;
}

看到remoteUser信息是从RPC连接中获取到的。

接下来看鉴权的过程fsd.isPermissionEnabled()

在NameNodeServer端获取到了remoteUGI后,就是和path的ugi信息做匹配了,很简单,就不一步步的分析了。

3 HDFS认证

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值