This is an automated email from the ASF dual-hosted git repository.
weichiu 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 05b184ae645 HDDS-14380. The user who starts Recon process will have
administrator privilege (#9627)
05b184ae645 is described below
commit 05b184ae6457abc575018d4061a3186a93518a1f
Author: Eric C. Ho <[email protected]>
AuthorDate: Fri Feb 27 07:41:21 2026 +0800
HDDS-14380. The user who starts Recon process will have administrator
privilege (#9627)
Co-authored-by: Doroszlai, Attila <[email protected]>
---
.../hadoop/ozone/recon/ReconControllerModule.java | 7 +++
.../org/apache/hadoop/ozone/recon/ReconServer.java | 47 ++++++++++++++++-
.../ozone/recon/api/filters/ReconAdminFilter.java | 27 +++-------
.../ozone/recon/api/filters/TestAdminFilter.java | 60 +++++++++++++++++++++-
4 files changed, 119 insertions(+), 22 deletions(-)
diff --git
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java
index 3f7e99056e4..9a9dfb48e74 100644
---
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java
+++
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconControllerModule.java
@@ -92,8 +92,15 @@ public class ReconControllerModule extends AbstractModule {
private static final Logger LOG =
LoggerFactory.getLogger(ReconControllerModule.class);
+ private final ReconServer reconServer;
+
+ public ReconControllerModule(ReconServer reconServer) {
+ this.reconServer = reconServer;
+ }
+
@Override
protected void configure() {
+ bind(ReconServer.class).toInstance(reconServer);
bind(OzoneConfiguration.class).toProvider(ConfigurationProvider.class);
bind(ReconHttpServer.class).in(Singleton.class);
bind(ReconStorageConfig.class).in(Singleton.class);
diff --git
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
index c9fc0ab3470..78a4938166a 100644
---
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
+++
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconServer.java
@@ -31,6 +31,7 @@
import com.google.inject.Injector;
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sql.DataSource;
@@ -38,9 +39,11 @@
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import
org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.recon.ReconConfig;
+import org.apache.hadoop.hdds.recon.ReconConfigKeys;
import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
import
org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
+import org.apache.hadoop.hdds.server.OzoneAdmins;
import org.apache.hadoop.hdds.utils.HddsServerUtil;
import org.apache.hadoop.ozone.OzoneSecurityUtil;
import org.apache.hadoop.ozone.recon.api.types.FeatureProvider;
@@ -85,6 +88,7 @@ public class ReconServer extends GenericCli implements
Callable<Void> {
private ReconStorageConfig reconStorage;
private CertificateClient certClient;
private ReconTaskStatusMetrics reconTaskStatusMetrics;
+ private OzoneAdmins reconAdmins;
private volatile boolean isStarted = false;
@@ -104,9 +108,18 @@ public Void call() throws Exception {
ReconServer.class, originalArgs, LOG, configuration);
ConfigurationProvider.setConfiguration(configuration);
+ try {
+ reconAdmins = createReconAdmins(configuration);
+ } catch (IOException e) {
+ LOG.error("Failed to identify current user for Recon admin
initialization. " +
+ "Please check Kerberos configuration or system user settings.", e);
+ throw e;
+ }
+ LOG.info("Recon start with adminUsers: {}",
reconAdmins.getAdminUsernames());
+
LOG.info("Initializing Recon server...");
try {
- injector = Guice.createInjector(new ReconControllerModule(),
+ injector = Guice.createInjector(new ReconControllerModule(this),
new ReconRestServletModule(configuration),
new ReconSchemaGenerationModule());
@@ -427,4 +440,36 @@ public ReconTaskController getReconTaskController() {
ReconHttpServer getHttpServer() {
return httpServer;
}
+
+ /**
+ * Creates OzoneAdmins for Recon with the starter user and configured admins.
+ *
+ * @param conf OzoneConfiguration
+ * @return OzoneAdmins instance
+ * @throws IOException if unable to get current user
+ */
+ private OzoneAdmins createReconAdmins(OzoneConfiguration conf) throws
IOException {
+ String starterUser =
UserGroupInformation.getCurrentUser().getShortUserName();
+ Collection<String> adminUsers =
+ OzoneAdmins.getOzoneAdminsFromConfig(conf, starterUser);
+ adminUsers.addAll(
+ conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS));
+
+ Collection<String> adminGroups =
+ OzoneAdmins.getOzoneAdminsGroupsFromConfig(conf);
+ adminGroups.addAll(
+
conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS));
+
+ return new OzoneAdmins(adminUsers, adminGroups);
+ }
+
+ /**
+ * Check if a user is a Recon administrator.
+ *
+ * @param user UserGroupInformation
+ * @return true if the user is an admin, false otherwise
+ */
+ public boolean isAdmin(UserGroupInformation user) {
+ return reconAdmins.isAdmin(user);
+ }
}
diff --git
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java
index 546a0b1949b..f4ae82b7d61 100644
---
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java
+++
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/filters/ReconAdminFilter.java
@@ -21,7 +21,6 @@
import com.google.inject.Singleton;
import java.io.IOException;
import java.security.Principal;
-import java.util.Collection;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -30,10 +29,7 @@
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.hdds.recon.ReconConfigKeys;
-import org.apache.hadoop.hdds.server.OzoneAdmins;
-import org.apache.hadoop.ozone.OzoneConfigKeys;
+import org.apache.hadoop.ozone.recon.ReconServer;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,15 +44,17 @@ public class ReconAdminFilter implements Filter {
private static final Logger LOG =
LoggerFactory.getLogger(ReconAdminFilter.class);
- private final OzoneConfiguration conf;
+ private final ReconServer reconServer;
@Inject
- ReconAdminFilter(OzoneConfiguration conf) {
- this.conf = conf;
+ ReconAdminFilter(ReconServer reconServer) {
+ this.reconServer = reconServer;
}
@Override
- public void init(FilterConfig filterConfig) throws ServletException { }
+ public void init(FilterConfig filterConfig) throws ServletException {
+ LOG.info("ReconAdminFilter initialized");
+ }
@Override
public void doFilter(ServletRequest servletRequest,
@@ -100,15 +98,6 @@ public void doFilter(ServletRequest servletRequest,
public void destroy() { }
private boolean hasPermission(UserGroupInformation user) {
- Collection<String> admins =
- conf.getStringCollection(OzoneConfigKeys.OZONE_ADMINISTRATORS);
- admins.addAll(
- conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS));
- Collection<String> adminGroups =
- conf.getStringCollection(OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS);
- adminGroups.addAll(
- conf.getStringCollection(
- ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS));
- return new OzoneAdmins(admins, adminGroups).isAdmin(user);
+ return reconServer.isAdmin(user);
}
}
diff --git
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java
index b6aa522d4e7..c8c8797b198 100644
---
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java
+++
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/filters/TestAdminFilter.java
@@ -20,12 +20,15 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.Sets;
+import java.io.IOException;
import java.security.Principal;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.FilterChain;
@@ -34,7 +37,9 @@
import javax.ws.rs.Path;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.recon.ReconConfigKeys;
+import org.apache.hadoop.hdds.server.OzoneAdmins;
import org.apache.hadoop.ozone.OzoneConfigKeys;
+import org.apache.hadoop.ozone.recon.ReconServer;
import org.apache.hadoop.ozone.recon.api.AdminOnly;
import org.apache.hadoop.ozone.recon.api.ClusterStateEndpoint;
import org.apache.hadoop.ozone.recon.api.MetricsProxyEndpoint;
@@ -170,8 +175,31 @@ public void testAdminFilterNoAdmins() throws Exception {
testAdminFilterWithPrincipal(new OzoneConfiguration(), "reject", false);
}
+ @Test
+ public void testAdminFilterStarterUserAutoAdmin() throws Exception {
+ OzoneConfiguration conf = new OzoneConfiguration();
+ String currentUser =
UserGroupInformation.getCurrentUser().getShortUserName();
+
+ testAdminFilterWithPrincipal(conf, currentUser, true);
+ testAdminFilterWithPrincipal(conf, "otheruser", false);
+ }
+
+ @Test
+ public void testAdminFilterStarterUserPlusConfiguredAdmins() throws
Exception {
+ OzoneConfiguration conf = new OzoneConfiguration();
+ conf.setStrings(OzoneConfigKeys.OZONE_ADMINISTRATORS, "configadmin");
+
+ String currentUser =
UserGroupInformation.getCurrentUser().getShortUserName();
+
+ testAdminFilterWithPrincipal(conf, currentUser, true);
+ testAdminFilterWithPrincipal(conf, "configadmin", true);
+ testAdminFilterWithPrincipal(conf, "reject", false);
+ }
+
private void testAdminFilterWithPrincipal(OzoneConfiguration conf,
String principalToUse, boolean shouldPass) throws Exception {
+ ReconServer mockReconServer = createMockReconServer(conf);
+
Principal mockPrincipal = mock(Principal.class);
when(mockPrincipal.getName()).thenReturn(principalToUse);
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
@@ -180,8 +208,9 @@ private void
testAdminFilterWithPrincipal(OzoneConfiguration conf,
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
FilterChain mockFilterChain = mock(FilterChain.class);
- new ReconAdminFilter(conf).doFilter(mockRequest, mockResponse,
- mockFilterChain);
+ ReconAdminFilter filter = new ReconAdminFilter(mockReconServer);
+ filter.init(null);
+ filter.doFilter(mockRequest, mockResponse, mockFilterChain);
if (shouldPass) {
verify(mockFilterChain).doFilter(mockRequest, mockResponse);
@@ -189,4 +218,31 @@ private void
testAdminFilterWithPrincipal(OzoneConfiguration conf,
verify(mockResponse).setStatus(HttpServletResponse.SC_FORBIDDEN);
}
}
+
+ /**
+ * Creates a mock ReconServer that mimics the actual admin initialization
logic.
+ */
+ private ReconServer createMockReconServer(OzoneConfiguration conf) throws
IOException {
+ String reconStarterUser =
UserGroupInformation.getCurrentUser().getShortUserName();
+ Collection<String> adminUsers =
+ OzoneAdmins.getOzoneAdminsFromConfig(conf, reconStarterUser);
+ adminUsers.addAll(
+ conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS));
+
+ Collection<String> adminGroups =
+ OzoneAdmins.getOzoneAdminsGroupsFromConfig(conf);
+ adminGroups.addAll(
+
conf.getStringCollection(ReconConfigKeys.OZONE_RECON_ADMINISTRATORS_GROUPS));
+
+ OzoneAdmins reconAdmins = new OzoneAdmins(adminUsers, adminGroups);
+
+ ReconServer mockReconServer = mock(ReconServer.class);
+ when(mockReconServer.isAdmin(any(UserGroupInformation.class)))
+ .thenAnswer(invocation -> {
+ UserGroupInformation user = invocation.getArgument(0);
+ return reconAdmins.isAdmin(user);
+ });
+
+ return mockReconServer;
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]