尝试模拟用户访问 HDFS 时出错

Error while trying to impersonate a user to access HDFS

我正在尝试代表另一个用户访问 HDFS。我正在尝试使用以下应用程序

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path; 
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.log4j.Logger;
import org.apache.hadoop.fs.FSDataOutputStream;
import java.security.PrivilegedExceptionAction;

public class HDFSProxyTest {

     public static void main (String[] args) throws Exception {
         String hadoopConfigurationPath = "/etc/hadoop/conf/";
         final  Configuration hdfsConfiguration = new Configuration();
         FileSystem localFileSystem = FileSystem.getLocal(hdfsConfiguration);
         Path coreSitePath = new Path(hadoopConfigurationPath+"core-site.xml");
         hdfsConfiguration.addResource(coreSitePath);
         Path hdfsSitePath = new Path(hadoopConfigurationPath+"hdfs-site.xml");
         hdfsConfiguration.addResource(hdfsSitePath);
         UserGroupInformation.setConfiguration(hdfsConfiguration);
         UserGroupInformation.loginUserFromKeytab("striim1@FCE.CLOUDERA.COM", "/home/striim/striim1_client.keytab");
         UserGroupInformation ugi =
          UserGroupInformation.createProxyUser("joy", UserGroupInformation.getLoginUser());
         FileSystem hadoopFileSystem =ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
             public FileSystem run() throws Exception {
                 return  FileSystem.get(hdfsConfiguration);
             }
         });

         FSDataOutputStream fsDataOutputStream = hadoopFileSystem.create(new Path("/user/striim1/hdfsproxy.csv"));
         fsDataOutputStream.write("This is niranjan!!! testing this\n".getBytes());
         fsDataOutputStream.close();
         hadoopFileSystem.close();
         }
}

此应用程序执行用户是 striim,我要模拟的超级用户是 striim1,他拥有 Kerberos 凭证和 joy 是我代表其尝试访问 HDFS 的用户。

我最终遇到了这个例外。

 2017-05-19 02:45:34,843 - WARN main org.apache.hadoop.util.NativeCodeLoader.<clinit> (NativeCodeLoader.java:62) Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Exception in thread "main" org.apache.hadoop.security.AccessControlException: Permission denied: user=joy, access=WRITE, inode="/user/striim1":striim1:striim1:drwxr-xr-x
    at org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider.checkFsPermission(DefaultAuthorizationProvider.java:281)
    at org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider.check(DefaultAuthorizationProvider.java:262)
    at org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider.check(DefaultAuthorizationProvider.java:242)
    at org.apache.hadoop.hdfs.server.namenode.DefaultAuthorizationProvider.checkPermission(DefaultAuthorizationProvider.java:169)
    at org.apache.sentry.hdfs.SentryAuthorizationProvider.checkPermission(SentryAuthorizationProvider.java:178)
    at org.apache.hadoop.hdfs.server.namenode.FSPermissionChecker.checkPermission(FSPermissionChecker.java:152)
    at org.apache.hadoop.hdfs.server.namenode.FSDirectory.checkPermission(FSDirectory.java:3560)
    at org.apache.hadoop.hdfs.server.namenode.FSDirectory.checkPermission(FSDirectory.java:3543)
    at org.apache.hadoop.hdfs.server.namenode.FSDirectory.checkAncestorAccess(FSDirectory.java:3525)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.checkAncestorAccess(FSNamesystem.java:6592)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFileInternal(FSNamesystem.java:2821)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFileInt(FSNamesystem.java:2739)
    at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFile(FSNamesystem.java:2624)
    at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.create(NameNodeRpcServer.java:599)
    at org.apache.hadoop.hdfs.server.namenode.AuthorizationProviderProxyClientProtocol.create(AuthorizationProviderProxyClientProtocol.java:112)
    at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.create(ClientNamenodeProtocolServerSideTranslatorPB.java:401)
    at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol.callBlockingMethod(ClientNamenodeProtocolProtos.java)
    at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:617)
    at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1073)
    at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2141)
    at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2137)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:422)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1714)
    at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2135)

这是我在core-site.xml中的配置

<property>
    <name>hadoop.proxyuser.striim1.hosts</name>
    <value>*</value>
  </property>
  <property>
    <name>hadoop.proxyuser.striim1.groups</name>
    <value>*</value>
  </property>

这是我要访问的文件夹的权限设置

drwxr-xr-x   - striim1       striim1                0 2017-05-19 02:50 /user/striim1

这个异常让我想到了以下问题

1) 即使我将超级用户的 UGI 传递给代理用户 joy。为什么客户端要在用户满意的情况下尝试创建文件?

2) 在我的集群部署中,“striim1”只是一个拥有 Kerberos 凭据的用户,并不是真正的超级用户 definition。仅当“striim1”是超级用户或已添加到超级用户组时才能模拟吗?

3) 我要模拟的用户名是否应该是有效的 OS 用户?如果不是会发生什么,在这方面做了什么验证?

4) 我尝试使用这个模拟用户编写的目录的权限设置应该是什么?它应该是拥有的某个位置还是超级用户组的一部分?

5) UGI.createProxyUser 是否应该在我的应用程序中显式调用?假设我从我想使用超级用户模拟的用户执行我的应用程序,并将代理用户配置(基本上传递 core-site.xm)传递给我的应用程序?这样就够了吗? (我期待通过将当前应用程序执行用户作为要模拟的用户在内部调用类似 createProxyUser 的东西)。

提前致谢。

此致,

尼兰詹

1) Even though I pass super user’s UGI to the proxy user joy. Why is the client trying to create the file in the context of user joy?

当使用代理用户功能调用 NameNode 等 HDFS 服务时,您将以 "real user" 的身份进行身份验证,然后调用就像是由代理用户或 "effective user" 完成的一样执行。在您的代码示例中,striim1 是真实用户,joy 是有效用户。这意味着此客户端代码使用 striim1 的 Kerberos 凭据向 NameNode 进行身份验证,然后它切换到表现得好像 joy 真的发出了调用。它会像创建文件一样快乐,正如您所见,这对于文件权限检查很重要。

您可能还想知道为什么即使您在 doAs 之外调用了 FileSystem#create,它仍然表现得像欢乐一样。这是因为 FileSystem 实例在创建时永久绑定到特定的 UserGroupInformation。由于您在 doAs 运行ning 中创建了实例作为代理用户 joy,因此对该 FileSystem 的后续操作将继续作为 joy 执行。

2) In my cluster deployment, “striim1” is just a user who has Kerberos credentials and not really a super-user as per this definition. Would impersonation work only if “striim1” is a superuser or added to the group of super user?

不要求真实用户必须是HDFS超级用户。您使用 striim1 的设置似乎工作正常,因为它作为 striim1(真实用户)进行身份验证,然后作为 joy(有效用户)执行。

3) Should the name of the user I’m trying to impersonate be a valid OS user? If not what would happen and what validation is done in this respect?

用户在服务器上存在 OS 级别并不是严格要求。这样做的结果是,当 NameNode 执行调用时,它将执行,就好像用户不是任何组的成员一样。 (组成员资格由 OS 集成确定,例如本地定义的组或 pam_ldap。)如果用户不需要拥有组成员资格来访问某些文件,那么这不会一个问题。

4) What should be the permission setting of directory I’m trying to write using this impersonated user? Should be it some location that is owned or part of super-user group?

在您的示例中,调用的执行就像用户很高兴一样。您可以自由选择满足您授予或拒绝访问 joy 的要求的任何文件权限设置。要在目录中创建新文件,用户必须对路径祖先的所有子组件(在您的示例中为 /、/user 和 /user/striim1)具有执行权限,并对直接祖先(/user/striim1 在你的例子中)。

有关此主题的更详细讨论,请参阅 HDFS Permissions Guide

5) Should UGI.createProxyUser be called explicitly in my application? Say I execute my application from the user whom I want to impersonate using super-user and I pass proxy user configuration (basically passing core-site.xm) to my application? Would this suffice? (l’m expecting something like createProxyUser being called internally by taking current app executing user as the user to be impersonated).

听起来您正在寻找一种解决方案,您无需专门为代理用户处理编写应用程序代码,而是可以在执行程序时从外部控制代理用户的使用。如果是这样,那么您可以通过将 HADOOP_PROXY_USER 环境变量设置为您要模拟的用户来控制它。例如,您可以 运行 kinit -kt 以 striim1 身份登录,然后 set HADOOP_PROXY_USER=joy 然后执行您的程序。

有关此功能实现的讨论,请参阅 HADOOP-8561。这是实现此功能的 UserGroupInformation 代码中的要点:

https://github.com/apache/hadoop/blob/release-2.7.0/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java#L803