Client 和 NN 创建Connection的详解(二)UserGroupInformation是如何传递的

本文详细介绍了Hadoop RPC框架中客户端与服务器端的连接建立过程,特别是用户身份验证和连接上下文的处理。在`processOneRpc`方法中,服务器会检查并处理连接请求,包括SASL协商、读取和授权`IpcConnectionContextProto`中的`UserGroupInformation`。在安全模式下,用户信息通过SASL认证确定,并可能涉及代理用户场景。如果认证协议为SIMPLE或KERBEROS,服务器会创建代理用户以便执行操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Client.Connection.setupIOStreams()

--->发送连接上下文

   writeConnectionContext(remoteId, authMethod);

发送的 IpcConnectionContextProto 里包含了UserGroupInformation

/**
 * Spec for UserInformationProto is specified in ProtoUtil#makeIpcConnectionContext
 */
message UserInformationProto {
  optional string effectiveUser = 1;
  optional string realUser = 2;
}

/**
 * The connection context is sent as part of the connection establishment.
 * It establishes the context for ALL Rpc calls within the connection.
 */
message IpcConnectionContextProto {
  // UserInfo beyond what is determined as part of security handshake 
  // at connection time (kerberos, tokens etc).
  optional UserInformationProto userInfo = 2;

  // Protocol name for next rpc layer.
  // The client created a proxy with this protocol name
  optional string protocol = 3;
}

在Server端的Connection.processOneRpc的过程中

private void processOneRpc(ByteBuffer bb)
        throws IOException, InterruptedException {
      // exceptions that escape this method are fatal to the connection.
      // setupResponse will use the rpc status to determine if the connection
      // should be closed.
      int callId = -1;
      int retry = RpcConstants.INVALID_RETRY_COUNT;
      try {
        final RpcWritable.Buffer buffer = RpcWritable.Buffer.wrap(bb);
        final RpcRequestHeaderProto header =
            getMessage(RpcRequestHeaderProto.getDefaultInstance(), buffer);
        callId = header.getCallId();
        retry = header.getRetryCount();
        if (LOG.isDebugEnabled()) {
          LOG.debug(" got #" + callId);
        }
        checkRpcHeaders(header);

        if (callId < 0) { // callIds typically used during connection setup
          //处理连接请求,ConnectionContext就是在这个方法里处理的
          processRpcOutOfBandRequest(header, buffer);
        } else if (!connectionContextRead) {
          throw new FatalRpcServerException(
              RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
              "Connection context not established");
        } else {
          processRpcRequest(header, buffer);
        }
      } 

/**
     * Establish RPC connection setup by negotiating SASL if required, then
     * reading and authorizing the connection header
     * @param header - RPC header
     * @param buffer - stream to request payload
     * @throws RpcServerException - setup failed due to SASL
     *         negotiation failure, premature or invalid connection context,
     *         or other state errors. This exception needs to be sent to the 
     *         client.
     * @throws IOException - failed to send a response back to the client
     * @throws InterruptedException
     */
    private void processRpcOutOfBandRequest(RpcRequestHeaderProto header,
        RpcWritable.Buffer buffer) throws RpcServerException,
            IOException, InterruptedException {
      final int callId = header.getCallId();
      if (callId == CONNECTION_CONTEXT_CALL_ID) {
        // SASL must be established prior to connection context
        if (authProtocol == AuthProtocol.SASL && !saslContextEstablished) {
          throw new FatalRpcServerException(
              RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
              "Connection header sent during SASL negotiation");
        }
        //处理ConnectionContext, 设置UserGroupInformation
        // read and authorize the user
        processConnectionContext(buffer);
      } else if (callId == AuthProtocol.SASL.callId) {
        // if client was switched to simple, ignore first SASL message
        if (authProtocol != AuthProtocol.SASL) {
          throw new FatalRpcServerException(
              RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
              "SASL protocol not requested by client");
        }
        saslReadAndProcess(buffer);
      } else if (callId == PING_CALL_ID) {
        LOG.debug("Received ping message");
      }

设置user为协议设置的用户,如果底层是通过Sasl 传输的,realUser是拥有kerbos认证的用户,它代理proxyUser执行操作。

/** Reads the connection context following the connection header
     * @throws RpcServerException - if the header cannot be
     *         deserialized, or the user is not authorized
     */ 
    private void processConnectionContext(RpcWritable.Buffer buffer)
        throws RpcServerException {
      // allow only one connection context during a session
      if (connectionContextRead) {
        throw new FatalRpcServerException(
            RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
            "Connection context already processed");
      }
      connectionContext = getMessage(IpcConnectionContextProto.getDefaultInstance(), buffer);
      protocolName = connectionContext.hasProtocol() ? connectionContext
          .getProtocol() : null;

      //获取连接上下文中的UserGroupInformation
      UserGroupInformation protocolUser = ProtoUtil.getUgi(connectionContext);
      //如果没有启动安全,Ugi就直接是传过来的用户
      if (authProtocol == AuthProtocol.NONE) {
        user = protocolUser;
      } else {
        //Ugi在processSasl的过程中,根据sasl的authorizationId设置的,参考方法 Conneciton.saslProcess()方法,里面会调用Connection.getAuthorizedUgi()方法,根据salsServer的authorizationId 创建
        // user is authenticated
        user.setAuthenticationMethod(authMethod);
        //Now we check if this is a proxy user case. If the protocol user is
        //different from the 'user', it is a proxy user scenario. However, 
        //this is not allowed if user authenticated with DIGEST.
        if ((protocolUser != null)
            && (!protocolUser.getUserName().equals(user.getUserName()))) {
          if (authMethod == AuthMethod.TOKEN) {
            // Not allowed to doAs if token authentication is used
            throw new FatalRpcServerException(
                RpcErrorCodeProto.FATAL_UNAUTHORIZED,
                new AccessControlException("Authenticated user (" + user
                    + ") doesn't match what the client claims to be ("
                    + protocolUser + ")"));
          } else {
            // Effective user can be different from authenticated user
            // for simple auth or kerberos auth
            // The user is the real user. Now we create a proxy user
            UserGroupInformation realUser = user;
            user = UserGroupInformation.createProxyUser(protocolUser
                .getUserName(), realUser);
          }
        }
      }
      authorizeConnection();
      // don't set until after authz because connection isn't established
      connectionContextRead = true;
      if (user != null) {
        connectionManager.incrUserConnections(user.getShortUserName());
      }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值