From 2c2b74c1c11b6531aabb1bf06782e859048d5983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andor=20Moln=C3=A1r?= Date: Tue, 3 Sep 2024 15:04:07 -0500 Subject: [PATCH] ZOOKEEPER-4851: Honor X-Forwarded-For optionally in IPAuthenticationProvider Reviewers: kezhuw, purushah Author: anmolnar Closes #2181 from anmolnar/ZOOKEEPER-4851 --- .../main/resources/markdown/zookeeperAdmin.md | 10 +++ .../server/auth/IPAuthenticationProvider.java | 6 ++ .../auth/IPAuthenticationProviderTest.java | 85 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticationProviderTest.java diff --git a/zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md b/zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md index 4b7fd0954f3..4096a1fdecf 100644 --- a/zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md +++ b/zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md @@ -1572,6 +1572,16 @@ and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/disp - 1. Regenerate `superDigest` when migrating to new algorithm. - 2. `SetAcl` for a znode which already had a digest auth of old algorithm. +* *IPAuthenticationProvider.skipxforwardedfor* : + (Java system property: **zookeeper.IPAuthenticationProvider.skipxforwardedfor**) + **New in 3.9.3:** + IPAuthenticationProvider needs the client IP address to authenticate the user. + By default, it tries to read **X-Forwarded-For** HTTP header first and if it's not + found, reads the **Host** header. Some proxy configuration requires this to + properly identify the client IP, but we can disable it relying only on the **Host** + header by setting this config option to **true**. + Default value is **false**. + * *X509AuthenticationProvider.superUser* : (Java system property: **zookeeper.X509AuthenticationProvider.superUser**) The SSL-backed way to enable a ZooKeeper ensemble diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java b/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java index 9334f7c5e72..c73d12f78c2 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java @@ -30,6 +30,8 @@ public class IPAuthenticationProvider implements AuthenticationProvider { public static final String X_FORWARDED_FOR_HEADER_NAME = "X-Forwarded-For"; + static final String SKIP_X_FORWARDED_FOR_KEY = "zookeeper.IPAuthenticationProvider.skipxforwardedfor"; + public String getScheme() { return "ip"; } @@ -150,6 +152,10 @@ public boolean isValid(String id) { * @return IP address */ public static String getClientIPAddress(final HttpServletRequest request) { + if (Boolean.getBoolean(SKIP_X_FORWARDED_FOR_KEY)) { + return request.getRemoteAddr(); + } + // to handle the case that a HTTP(s) client connects via a proxy or load balancer final String xForwardedForHeader = request.getHeader(X_FORWARDED_FOR_HEADER_NAME); if (xForwardedForHeader == null) { diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticationProviderTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticationProviderTest.java new file mode 100644 index 00000000000..fc814f7ea5b --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticationProviderTest.java @@ -0,0 +1,85 @@ +/* + * 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.zookeeper.server.auth; + +import static org.apache.zookeeper.server.auth.IPAuthenticationProvider.SKIP_X_FORWARDED_FOR_KEY; +import static org.apache.zookeeper.server.auth.IPAuthenticationProvider.X_FORWARDED_FOR_HEADER_NAME; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import javax.servlet.http.HttpServletRequest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class IPAuthenticationProviderTest { + + private HttpServletRequest request; + + @Before + public void setUp() throws Exception { + System.clearProperty(SKIP_X_FORWARDED_FOR_KEY); + request = mock(HttpServletRequest.class); + } + + @After + public void tearDown() { + System.clearProperty(SKIP_X_FORWARDED_FOR_KEY); + } + + @Test + public void testGetClientIPAddressSkipXForwardedFor() { + // Arrange + System.setProperty(SKIP_X_FORWARDED_FOR_KEY, "true"); + doReturn("192.168.1.1").when(request).getRemoteAddr(); + doReturn("192.168.1.2,192.168.1.3,192.168.1.4").when(request).getHeader(X_FORWARDED_FOR_HEADER_NAME); + + // Act + String clientIp = IPAuthenticationProvider.getClientIPAddress(request); + + // Assert + assertEquals("192.168.1.1", clientIp); + } + + @Test + public void testGetClientIPAddressWithXForwardedFor() { + // Arrange + System.setProperty(SKIP_X_FORWARDED_FOR_KEY, "false"); + doReturn("192.168.1.1").when(request).getRemoteAddr(); + doReturn("192.168.1.2,192.168.1.3,192.168.1.4").when(request).getHeader(X_FORWARDED_FOR_HEADER_NAME); + + // Act + String clientIp = IPAuthenticationProvider.getClientIPAddress(request); + + // Assert + assertEquals("192.168.1.2", clientIp); + } + + @Test + public void testGetClientIPAddressMissingXForwardedFor() { + // Arrange + System.setProperty(SKIP_X_FORWARDED_FOR_KEY, "false"); + doReturn("192.168.1.1").when(request).getRemoteAddr(); + + // Act + String clientIp = IPAuthenticationProvider.getClientIPAddress(request); + + // Assert + assertEquals("192.168.1.1", clientIp); + } +}