This is an automated email from the ASF dual-hosted git repository.

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 7a138954ee HDDS-9663. Allow OM to detect client address when using 
gRPC (#5590)
7a138954ee is described below

commit 7a138954ee628e161c1ce857e82e8a5117ff00ab
Author: Slava Tutrinov <[email protected]>
AuthorDate: Sat Nov 18 11:56:09 2023 +0300

    HDDS-9663. Allow OM to detect client address when using gRPC (#5590)
---
 hadoop-ozone/common/pom.xml                        |  5 ++
 .../ozone/om/protocolPB/GrpcOmTransport.java       | 21 ++++--
 .../grpc/ClientAddressClientInterceptor.java       | 55 +++++++++++++++
 .../grpc/ClientAddressServerInterceptor.java       | 48 +++++++++++++
 .../om/protocolPB/grpc/GrpcClientConstants.java    | 44 ++++++++++++
 .../ozone/om/protocolPB/grpc/package-info.java     | 22 ++++++
 .../grpc/TestClientAddressClientInterceptor.java   | 79 ++++++++++++++++++++++
 .../grpc/TestClientAddressServerInterceptor.java   | 78 +++++++++++++++++++++
 .../hadoop/ozone/om/GrpcOzoneManagerServer.java    |  2 +
 .../apache/hadoop/ozone/om/OmMetadataReader.java   | 13 +++-
 .../hadoop/ozone/om/request/OMClientRequest.java   |  9 +++
 .../hadoop/ozone/om/TestOMMetadataReader.java      | 74 ++++++++++++++++++++
 .../ozone/om/request/OMRequestTestUtils.java       | 16 +++++
 .../request/TestOMClientRequestWithUserInfo.java   | 47 +++++++++++--
 14 files changed, 502 insertions(+), 11 deletions(-)

diff --git a/hadoop-ozone/common/pom.xml b/hadoop-ozone/common/pom.xml
index d074c36060..e48fe801c9 100644
--- a/hadoop-ozone/common/pom.xml
+++ b/hadoop-ozone/common/pom.xml
@@ -61,6 +61,11 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd";>
       <artifactId>mockito-core</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-inline</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>ch.qos.reload4j</groupId>
       <artifactId>reload4j</artifactId>
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
index 74a6422f5c..96f7b48665 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
@@ -19,6 +19,7 @@ package org.apache.hadoop.ozone.om.protocolPB;
 
 import java.io.IOException;
 import java.lang.reflect.Constructor;
+import java.net.InetAddress;
 import java.security.cert.X509Certificate;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -29,6 +30,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import com.google.common.net.HostAndPort;
+import io.grpc.Context;
 import io.grpc.Status;
 import io.grpc.StatusRuntimeException;
 import org.apache.hadoop.ipc.RemoteException;
@@ -43,6 +45,8 @@ import org.apache.hadoop.io.retry.RetryPolicy;
 import org.apache.hadoop.ozone.OzoneConfigKeys;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
+import 
org.apache.hadoop.ozone.om.protocolPB.grpc.ClientAddressClientInterceptor;
+import org.apache.hadoop.ozone.om.protocolPB.grpc.GrpcClientConstants;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
 import org.apache.hadoop.security.UserGroupInformation;
@@ -160,7 +164,9 @@ public class GrpcOmTransport implements OmTransport {
         channelBuilder.usePlaintext();
       }
 
-      channels.put(hostaddr, channelBuilder.build());
+      channels.put(hostaddr,
+          channelBuilder.intercept(new ClientAddressClientInterceptor())
+              .build());
       clients.put(hostaddr,
           OzoneManagerServiceGrpc
               .newBlockingStub(channels.get(hostaddr)));
@@ -175,7 +181,7 @@ public class GrpcOmTransport implements OmTransport {
 
   @Override
   public OMResponse submitRequest(OMRequest payload) throws IOException {
-    OMResponse resp = null;
+    AtomicReference<OMResponse> resp = new AtomicReference<>();
     boolean tryOtherHost = true;
     int expectedFailoverCount = 0;
     ResultCodes resultCode = ResultCodes.INTERNAL_ERROR;
@@ -183,7 +189,14 @@ public class GrpcOmTransport implements OmTransport {
       tryOtherHost = false;
       expectedFailoverCount = syncFailoverCount.get();
       try {
-        resp = clients.get(host.get()).submitRequest(payload);
+        InetAddress inetAddress = InetAddress.getLocalHost();
+        Context.current()
+            .withValue(GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY,
+                inetAddress.getHostAddress())
+            .withValue(GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY,
+                inetAddress.getHostName())
+            .run(() -> resp.set(clients.get(host.get())
+                .submitRequest(payload)));
       } catch (StatusRuntimeException e) {
         LOG.error("Failed to submit request", e);
         if (e.getStatus().getCode() == Status.Code.UNAVAILABLE) {
@@ -201,7 +214,7 @@ public class GrpcOmTransport implements OmTransport {
         }
       }
     }
-    return resp;
+    return resp.get();
   }
 
   private Exception unwrapException(Exception ex) {
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressClientInterceptor.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressClientInterceptor.java
new file mode 100644
index 0000000000..5941309727
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressClientInterceptor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ozone.om.protocolPB.grpc;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+
+/**
+ * GRPC client side interceptor to provide client hostname and IP address.
+ */
+public class ClientAddressClientInterceptor implements ClientInterceptor {
+  @Override
+  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
+      MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions,
+      Channel channel) {
+    return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
+        channel.newCall(methodDescriptor, callOptions)) {
+      @Override
+      public void start(Listener<RespT> responseListener, Metadata headers) {
+        String ipAddress = GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY.get();
+        if (ipAddress != null) {
+          headers.put(GrpcClientConstants.CLIENT_HOSTNAME_METADATA_KEY,
+              ipAddress);
+        }
+        String hostname = GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY.get();
+        if (GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY.get() != null) {
+          headers.put(GrpcClientConstants.CLIENT_IP_ADDRESS_METADATA_KEY,
+              hostname);
+        }
+        super.start(responseListener, headers);
+      }
+    };
+  }
+}
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressServerInterceptor.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressServerInterceptor.java
new file mode 100644
index 0000000000..d1f4f1a4db
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/ClientAddressServerInterceptor.java
@@ -0,0 +1,48 @@
+/*
+ * 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.ozone.om.protocolPB.grpc;
+
+import io.grpc.Context;
+import io.grpc.Contexts;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+
+/**
+ * GRPC server side interceptor to retrieve the client IP and hostname.
+ */
+public class ClientAddressServerInterceptor implements ServerInterceptor {
+  @Override
+  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
+      ServerCall<ReqT, RespT> call, Metadata headers,
+      ServerCallHandler<ReqT, RespT> next) {
+    String clientHostname =
+        headers.get(GrpcClientConstants.CLIENT_HOSTNAME_METADATA_KEY);
+    String clientIpAddress =
+        headers.get(GrpcClientConstants.CLIENT_IP_ADDRESS_METADATA_KEY);
+
+    Context ctx = Context.current()
+        .withValue(GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY,
+            clientHostname)
+        .withValue(GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY,
+            clientIpAddress);
+    return Contexts.interceptCall(ctx, call, headers, next);
+  }
+}
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/GrpcClientConstants.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/GrpcClientConstants.java
new file mode 100644
index 0000000000..4396108067
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/GrpcClientConstants.java
@@ -0,0 +1,44 @@
+/*
+ * 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.ozone.om.protocolPB.grpc;
+
+import io.grpc.Context;
+import io.grpc.Metadata;
+
+/**
+ * Constants to store grpc-client specific header values.
+ */
+public final class GrpcClientConstants {
+
+  private GrpcClientConstants() {
+  }
+
+  public static final Context.Key<String> CLIENT_HOSTNAME_CTX_KEY =
+      Context.key("CLIENT_HOSTNAME");
+
+  public static final Metadata.Key<String> CLIENT_HOSTNAME_METADATA_KEY =
+      Metadata.Key.of("CLIENT_HOSTNAME", Metadata.ASCII_STRING_MARSHALLER);
+
+  public static final Context.Key<String> CLIENT_IP_ADDRESS_CTX_KEY =
+      Context.key("CLIENT_IP_ADDRESS");
+
+  public static final Metadata.Key<String> CLIENT_IP_ADDRESS_METADATA_KEY =
+      Metadata.Key.of("CLIENT_IP_ADDRESS", Metadata.ASCII_STRING_MARSHALLER);
+
+}
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/package-info.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/package-info.java
new file mode 100644
index 0000000000..f4d76862a1
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/grpc/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+/**
+ * This package contains grpc interceptors and their related classes.
+ * To provide specific grpc headers
+ */
+package org.apache.hadoop.ozone.om.protocolPB.grpc;
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressClientInterceptor.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressClientInterceptor.java
new file mode 100644
index 0000000000..f8e95cfa80
--- /dev/null
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressClientInterceptor.java
@@ -0,0 +1,79 @@
+/**
+ * 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.ozone.om.protocolPB.grpc;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.Context;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test OM GRPC client interceptor to define client ip and hostname headers.
+ */
+public class TestClientAddressClientInterceptor {
+
+  @Test
+  public void testClientAddressEntriesInRequestHeaders() {
+    try (MockedStatic<Context> grpcContextStaticMock =
+             Mockito.mockStatic(Context.class)) {
+      // given
+      Context.Key<String> ipAddressContextKey = mock(Context.Key.class);
+      when(ipAddressContextKey.get()).thenReturn("172.43.3.2");
+      grpcContextStaticMock.when(() -> Context.key("CLIENT_IP_ADDRESS"))
+          .thenReturn(ipAddressContextKey);
+
+      Context.Key<String> hostnameContextKey = mock(Context.Key.class);
+      when(hostnameContextKey.get()).thenReturn("host.example.com");
+      grpcContextStaticMock.when(() -> Context.key("CLIENT_HOSTNAME"))
+          .thenReturn(hostnameContextKey);
+
+      ClientInterceptor interceptor = new ClientAddressClientInterceptor();
+      Channel channel = mock(Channel.class);
+      MethodDescriptor methodDescriptor = mock(MethodDescriptor.class);
+      CallOptions callOptions = mock(CallOptions.class);
+      ClientCall delegate = mock(ClientCall.class);
+      when(channel.newCall(eq(methodDescriptor), eq(callOptions)))
+          .thenReturn(delegate);
+
+      // when
+      ClientCall clientCall = interceptor.interceptCall(methodDescriptor,
+          callOptions, channel);
+      Metadata metadata = mock(Metadata.class);
+      clientCall.start(mock(ClientCall.Listener.class), metadata);
+
+      // then
+      verify(metadata).put(GrpcClientConstants.CLIENT_HOSTNAME_METADATA_KEY,
+          hostnameContextKey.get());
+      verify(metadata).put(GrpcClientConstants.CLIENT_IP_ADDRESS_METADATA_KEY,
+          ipAddressContextKey.get());
+    }
+  }
+
+}
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressServerInterceptor.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressServerInterceptor.java
new file mode 100644
index 0000000000..d8a838440d
--- /dev/null
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/grpc/TestClientAddressServerInterceptor.java
@@ -0,0 +1,78 @@
+/**
+ * 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.ozone.om.protocolPB.grpc;
+
+import io.grpc.Context;
+import io.grpc.Contexts;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test OM GRPC server interceptor to define client ip and hostname.
+ */
+public class TestClientAddressServerInterceptor {
+
+  @Test
+  public void testClientAddressEntriesInHeaders() {
+    try (MockedStatic<Contexts> contextsMockedStatic =
+             Mockito.mockStatic(Contexts.class)) {
+      // given
+      ServerInterceptor serverInterceptor =
+          new ClientAddressServerInterceptor();
+      ServerCall serverCall = mock(ServerCall.class);
+      ServerCallHandler serverCallHandler = mock(ServerCallHandler.class);
+      Metadata headers = mock(Metadata.class);
+      when(headers.get(GrpcClientConstants.CLIENT_HOSTNAME_METADATA_KEY))
+          .thenReturn("host.example.com");
+      when(headers.get(GrpcClientConstants.CLIENT_IP_ADDRESS_METADATA_KEY))
+          .thenReturn("173.56.23.4");
+
+      // when
+      serverInterceptor.interceptCall(serverCall, headers, serverCallHandler);
+
+      // then
+      ArgumentCaptor<Context> contextArgumentCaptor =
+          ArgumentCaptor.forClass(Context.class);
+      contextsMockedStatic.verify(
+          () -> {
+            Contexts.interceptCall(contextArgumentCaptor.capture(),
+                eq(serverCall), eq(headers), eq(serverCallHandler));
+          }
+      );
+      Context context = contextArgumentCaptor.getValue();
+      context.attach();
+      Assertions.assertEquals("host.example.com",
+          GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY.get());
+      Assertions.assertEquals("173.56.23.4",
+          GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY.get());
+    }
+  }
+
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/GrpcOzoneManagerServer.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/GrpcOzoneManagerServer.java
index 7753f94a07..80085db7c6 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/GrpcOzoneManagerServer.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/GrpcOzoneManagerServer.java
@@ -35,6 +35,7 @@ import 
org.apache.hadoop.ozone.grpc.metrics.GrpcMetricsServerResponseInterceptor
 import org.apache.hadoop.ozone.grpc.metrics.GrpcMetricsServerTransportFilter;
 import org.apache.hadoop.ozone.ha.ConfUtils;
 import org.apache.hadoop.ozone.grpc.metrics.GrpcMetrics;
+import 
org.apache.hadoop.ozone.om.protocolPB.grpc.ClientAddressServerInterceptor;
 import 
org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
 import org.apache.hadoop.ozone.om.protocolPB.GrpcOmTransport;
 import org.apache.hadoop.ozone.security.OzoneDelegationTokenSecretManager;
@@ -153,6 +154,7 @@ public class GrpcOzoneManagerServer {
             new OzoneManagerServiceGrpc(omTranslator,
                 delegationTokenMgr,
                 omServerConfig),
+            new ClientAddressServerInterceptor(),
             new GrpcMetricsServerResponseInterceptor(omS3gGrpcMetrics),
             new GrpcMetricsServerRequestInterceptor(omS3gGrpcMetrics)))
         .addTransportFilter(
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
index 5d324cae62..6b390ccbba 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java
@@ -39,6 +39,7 @@ import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
 import org.apache.hadoop.ozone.om.helpers.OzoneFileStatusLight;
 import org.apache.hadoop.ozone.om.helpers.S3VolumeContext;
+import org.apache.hadoop.ozone.om.protocolPB.grpc.GrpcClientConstants;
 import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
 import org.apache.hadoop.ozone.security.acl.RequestContext;
 import org.apache.hadoop.security.UserGroupInformation;
@@ -565,7 +566,13 @@ public class OmMetadataReader implements 
IOmMetadataReader, Auditor {
   static String getClientAddress() {
     String clientMachine = Server.getRemoteAddress();
     if (clientMachine == null) { //not a RPC client
-      clientMachine = "";
+      String clientIpAddress =
+          GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY.get();
+      if (clientIpAddress != null) {
+        clientMachine = clientIpAddress;
+      } else {
+        clientMachine = "";
+      }
     }
     return clientMachine;
   }
@@ -576,7 +583,7 @@ public class OmMetadataReader implements IOmMetadataReader, 
Auditor {
 
     return new AuditMessage.Builder()
         .setUser(getRemoteUserName())
-        .atIp(Server.getRemoteAddress())
+        .atIp(getClientAddress())
         .forOperation(op)
         .withParams(auditMap)
         .withResult(AuditEventStatus.SUCCESS)
@@ -589,7 +596,7 @@ public class OmMetadataReader implements IOmMetadataReader, 
Auditor {
 
     return new AuditMessage.Builder()
         .setUser(getRemoteUserName())
-        .atIp(Server.getRemoteAddress())
+        .atIp(getClientAddress())
         .forOperation(op)
         .withParams(auditMap)
         .withResult(AuditEventStatus.FAILURE)
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
index 48c68cb5aa..642faefa05 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
@@ -35,6 +35,7 @@ import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.OzonePrefixPathImpl;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
+import org.apache.hadoop.ozone.om.protocolPB.grpc.GrpcClientConstants;
 import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
 import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
 import org.apache.hadoop.ozone.om.response.OMClientResponse;
@@ -165,9 +166,17 @@ public abstract class OMClientRequest implements 
RequestAuditor {
       userInfo.setUserName(omRequest.getUserInfo().getUserName());
     }
 
+    String grpcContextClientIpAddress =
+        GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY.get();
+    String grpcContextClientHostname =
+        GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY.get();
     if (remoteAddress != null) {
       userInfo.setHostName(remoteAddress.getHostName());
       userInfo.setRemoteAddress(remoteAddress.getHostAddress()).build();
+    } else if (grpcContextClientHostname != null
+        && grpcContextClientIpAddress != null) {
+      userInfo.setHostName(grpcContextClientHostname);
+      userInfo.setRemoteAddress(grpcContextClientIpAddress);
     }
 
     return userInfo.build();
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMetadataReader.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMetadataReader.java
new file mode 100644
index 0000000000..e4e3d544a8
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMetadataReader.java
@@ -0,0 +1,74 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.om;
+
+import io.grpc.Context;
+import org.apache.hadoop.ipc.Server;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test ozone metadata reader.
+ */
+public class TestOMMetadataReader {
+
+  @Test
+  public void testGetClientAddress() {
+    try (
+        MockedStatic<Server> ipcServerStaticMock =
+            Mockito.mockStatic(Server.class);
+        MockedStatic<Context> grpcRequestContextStaticMock =
+            Mockito.mockStatic(Context.class);
+    ) {
+      // given
+      String expectedClientAddressInCaseOfHadoopRpcCall =
+          "hadoop.ipc.client.com";
+      ipcServerStaticMock.when(Server::getRemoteAddress)
+          .thenReturn(null, null, expectedClientAddressInCaseOfHadoopRpcCall);
+
+      String expectedClientAddressInCaseOfGrpcCall = "172.45.23.4";
+      Context.Key<String> clientIpAddressKey = mock(Context.Key.class);
+      when(clientIpAddressKey.get())
+          .thenReturn(expectedClientAddressInCaseOfGrpcCall, null);
+
+      grpcRequestContextStaticMock.when(() -> Context.key("CLIENT_IP_ADDRESS"))
+          .thenReturn(clientIpAddressKey);
+
+      // when (GRPC call with defined client address)
+      String clientAddress = OmMetadataReader.getClientAddress();
+      // then
+      assertEquals(expectedClientAddressInCaseOfGrpcCall, clientAddress);
+
+      // and when (GRPC call without client address)
+      clientAddress = OmMetadataReader.getClientAddress();
+      // then
+      assertEquals("", clientAddress);
+
+      // and when (Hadoop RPC client call)
+      clientAddress = OmMetadataReader.getClientAddress();
+      // then
+      assertEquals(expectedClientAddressInCaseOfHadoopRpcCall, clientAddress);
+    }
+  }
+
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
index 3e3433fab4..45209258f7 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
@@ -1605,4 +1605,20 @@ public final class OMRequestTestUtils {
         .thenReturn(validator);
     doCallRealMethod().when(ozoneManager).validateReplicationConfig(any());
   }
+
+  public static OMRequest createRequestWithS3Credentials(String accessId,
+                                                         String signature,
+                                                         String stringToSign) {
+    return OMRequest.newBuilder()
+        .setS3Authentication(
+            OzoneManagerProtocolProtos.S3Authentication.newBuilder()
+                .setAccessId(accessId)
+                .setSignature(signature)
+                .setStringToSign(stringToSign)
+                .build())
+        .setCmdType(Type.CommitKey)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java
index cb68a8b939..299a8d5652 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java
@@ -19,18 +19,23 @@
 
 package org.apache.hadoop.ozone.om.request;
 
+import java.io.IOException;
 import java.net.InetAddress;
 import java.nio.file.Path;
 import java.util.UUID;
 
+import io.grpc.Context;
 import mockit.Mock;
 import mockit.MockUp;
+import org.apache.hadoop.ozone.om.helpers.BucketLayout;
+import org.apache.hadoop.ozone.om.request.key.OMKeyCommitRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketInfo;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
@@ -44,8 +49,10 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import org.apache.hadoop.ozone.om.request.bucket.OMBucketCreateRequest;
 import org.apache.hadoop.security.UserGroupInformation;
 
+import static 
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.createRequestWithS3Credentials;
 import static 
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.newBucketInfoBuilder;
 import static 
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.newCreateBucketRequest;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 /**
@@ -75,7 +82,10 @@ public class TestOMClientRequestWithUserInfo {
     when(ozoneManager.getMetrics()).thenReturn(omMetrics);
     when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
     inetAddress = InetAddress.getByName("127.0.0.1");
+  }
 
+  @Test
+  public void testUserInfoInCaseOfHadoopTransport() throws Exception {
     new MockUp<ProtobufRpcEngine.Server>() {
       @Mock
       public UserGroupInformation getRemoteUser() {
@@ -91,10 +101,6 @@ public class TestOMClientRequestWithUserInfo {
         return inetAddress;
       }
     };
-  }
-
-  @Test
-  public void testUserInfo() throws Exception {
 
     String bucketName = UUID.randomUUID().toString();
     String volumeName = UUID.randomUUID().toString();
@@ -131,4 +137,37 @@ public class TestOMClientRequestWithUserInfo {
         ugi.getUserName());
     Assertions.assertEquals(inetAddress.getHostName(), hostName);
   }
+
+  @Test
+  public void testUserInfoInCaseOfGrpcTransport() throws IOException {
+    try (MockedStatic<Context> mockedGrpcRequestContextKey =
+             Mockito.mockStatic(Context.class)) {
+      // given
+      Context.Key<String> hostnameKey = mock(Context.Key.class);
+      when(hostnameKey.get()).thenReturn("hostname");
+
+      Context.Key<String> ipAddress = mock(Context.Key.class);
+      when(ipAddress.get()).thenReturn("172.5.3.5");
+
+      mockedGrpcRequestContextKey.when(() -> Context.key("CLIENT_HOSTNAME"))
+          .thenReturn(hostnameKey);
+      mockedGrpcRequestContextKey.when(() -> Context.key("CLIENT_IP_ADDRESS"))
+          .thenReturn(ipAddress);
+
+      OMRequest s3SignedOMRequest = createRequestWithS3Credentials("AccessId",
+          "Signature", "StringToSign");
+      OMClientRequest omClientRequest =
+          new OMKeyCommitRequest(s3SignedOMRequest, mock(BucketLayout.class));
+
+      // when
+      OzoneManagerProtocolProtos.UserInfo userInfo =
+          omClientRequest.getUserInfo();
+
+      // then
+      Assertions.assertEquals("hostname", userInfo.getHostName());
+      Assertions.assertEquals("172.5.3.5", userInfo.getRemoteAddress());
+      Assertions.assertEquals("AccessId", userInfo.getUserName());
+    }
+  }
+
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to