Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDDS-11041. Add admin request filter for S3 requests and UGI support for GrpcOmTransport #7268

Merged
merged 35 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8a242b0
HDDS-11041. Add admin request filter for S3 requests and passing UGI …
devabhishekpal Oct 4, 2024
b9002fb
Fixed checkstyle issues
devabhishekpal Oct 4, 2024
d5272f5
Using OzoneConfigUtils to fetch s3 admins
devabhishekpal Oct 4, 2024
932fffc
Addressed codestyle review comments
devabhishekpal Oct 4, 2024
a0ac299
Add common utils to fetch S3 admin users
devabhishekpal Oct 6, 2024
065f9fd
Added unauthorized test scenarios and fixed code style
devabhishekpal Oct 6, 2024
4a3c3d5
Fixed findbug issues
devabhishekpal Oct 6, 2024
cf3c4e3
Mock ContainerRequestContext
devabhishekpal Oct 6, 2024
ff14d26
Addressed review comments
devabhishekpal Oct 10, 2024
8e93f5c
Addressed review comments
devabhishekpal Oct 11, 2024
44bad3b
Addressed checkstyle and RAT issues
devabhishekpal Oct 11, 2024
e0e8748
Addressed checkstyle issues
devabhishekpal Oct 11, 2024
664489a
Fixed test verification and RPC failover config
devabhishekpal Oct 14, 2024
f8a4e2b
Fixed tests
devabhishekpal Oct 14, 2024
0a32667
Fixed checkstyles
devabhishekpal Oct 14, 2024
db908fb
Fixed checkstyles
devabhishekpal Oct 14, 2024
2daecc9
Added test coverage for S3 admin utils
devabhishekpal Oct 20, 2024
e67269e
Fixed checkstyle issue
devabhishekpal Oct 20, 2024
d2653a5
Fixed build issues
devabhishekpal Oct 20, 2024
9c9a9da
Fixed checkstyle issues
devabhishekpal Oct 21, 2024
fb6dcb9
Fix tests
devabhishekpal Oct 21, 2024
30e55e2
Add newline at file end for checkstyles
devabhishekpal Oct 21, 2024
f4cfed4
Remove print line
devabhishekpal Oct 21, 2024
ad12dfe
Re-enable robot tests and add robot tests for non-admin user
devabhishekpal Oct 21, 2024
bf6fb02
Refactor S3 config utils to hdds-framework
devabhishekpal Oct 21, 2024
e2f9248
Fixed checkstyle issues
devabhishekpal Oct 21, 2024
2acffda
Refactored createOMProxy to parent provider and enabled GrpcOmTranspo…
devabhishekpal Oct 23, 2024
c52deae
Removed unused imports and variables
devabhishekpal Oct 23, 2024
3a60637
Add fallback value to OM transport and set it to Hadoop3OmTransport i…
devabhishekpal Oct 23, 2024
9e8c64e
Addressed review comments
devabhishekpal Oct 24, 2024
c674834
Fixed checkstyles
devabhishekpal Oct 24, 2024
bd8e47d
Fixed test issues
devabhishekpal Oct 24, 2024
c331c99
Fixed checkstyle issues
devabhishekpal Oct 24, 2024
e569e5f
Addressed review comments
devabhishekpal Oct 25, 2024
bf5b181
Fix findbugs
adoroszlai Oct 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
*/
package org.apache.hadoop.hdds.server;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import com.google.common.collect.Sets;

import jakarta.annotation.Nullable;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;
Expand All @@ -33,6 +35,9 @@
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS_WILDCARD;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS_GROUPS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_S3_ADMINISTRATORS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_S3_ADMINISTRATORS_GROUPS;

devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved

/**
* This class contains ozone admin user information, username and group,
Expand Down Expand Up @@ -186,4 +191,85 @@ public static Collection<String> getOzoneReadOnlyAdminsGroupsFromConfig(
return conf.getTrimmedStringCollection(
OZONE_READONLY_ADMINISTRATORS_GROUPS);
}

/**
* Get the list of S3 administrators from Ozone config.
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
*
* @param conf An instance of {@link OzoneConfiguration} being used
* @return A {@link Collection} of the S3 administrator users
*
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
* If ozone.s3.administrators value is empty string or unset,
* defaults to ozone.administrators value.
*/
public static Collection<String> getS3AdminsFromConfig(OzoneConfiguration conf) throws IOException {
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
Collection<String> ozoneAdmins = conf.getTrimmedStringCollection(OZONE_S3_ADMINISTRATORS);

if (ozoneAdmins == null || ozoneAdmins.isEmpty()) {
ozoneAdmins = conf.getTrimmedStringCollection(OZONE_ADMINISTRATORS);
}
String omSPN = UserGroupInformation.getCurrentUser().getShortUserName();
if (!ozoneAdmins.contains(omSPN)) {
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
ozoneAdmins.add(omSPN);
}

return ozoneAdmins;
}

/**
* Get the list of the groups that are a part S3 administrators from Ozone config.
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
*
* @param conf An instance of {@link OzoneConfiguration} being used
* @return A {@link Collection} of the S3 administrator groups
*
* If ozone.s3.administrators.groups value is empty or unset,
* defaults to the ozone.administrators.groups value
*/
public static Collection<String> getS3AdminsGroupsFromConfig(OzoneConfiguration conf) {
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
Collection<String> s3AdminsGroup = conf.getTrimmedStringCollection(OZONE_S3_ADMINISTRATORS_GROUPS);

if (s3AdminsGroup.isEmpty() && conf.getTrimmedStringCollection(OZONE_S3_ADMINISTRATORS).isEmpty()) {
s3AdminsGroup = conf.getTrimmedStringCollection(OZONE_ADMINISTRATORS_GROUPS);
}

return s3AdminsGroup;
}

/**
* Get the users and groups that are a part of S3 administrators.
* @param conf Stores an instance of {@link OzoneConfiguration} being used
* @return an instance of {@link OzoneAdmins} containing the S3 admin users and groups
*/
public static OzoneAdmins getS3Admins(OzoneConfiguration conf) {
Collection<String> s3Admins;
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
try {
s3Admins = getS3AdminsFromConfig(conf);
} catch (IOException ie) {
s3Admins = null;
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
}
Collection<String> s3AdminGroups = getS3AdminsGroupsFromConfig(conf);

return new OzoneAdmins(s3Admins, s3AdminGroups);
}

/**
* Check if the provided user is an S3 administrator.
* @param user An instance of {@link UserGroupInformation} with information about the user to verify
* @param s3Admins An instance of {@link OzoneAdmins} containing information
* of the S3 administrator users and groups in the system
* @return {@code true} if the provided user is an S3 administrator else {@code false}
*/
public static boolean isS3Admin(@Nullable UserGroupInformation user, OzoneAdmins s3Admins) {
return null != user && s3Admins.isAdmin(user);
}

/**
* Check if the provided user is an S3 administrator.
* @param user An instance of {@link UserGroupInformation} with information about the user to verify
* @param conf An instance of {@link OzoneConfiguration} being used
* @return {@code true} if the provided user is an S3 administrator else {@code false}
*/
public static boolean isS3Admin(@Nullable UserGroupInformation user, OzoneConfiguration conf) {
OzoneAdmins s3Admins = getS3Admins(conf);
return isS3Admin(user, s3Admins);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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.hdds.server;

import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

/**
* This class is to test the utilities present in the OzoneAdmins class.
*/
class TestOzoneAdmins {
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
// The following set of tests are to validate the S3 based utilities present in OzoneAdmins
@Test
void testS3AdminExtraction() throws IOException {
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS, "alice,bob");

assertThat(OzoneAdmins.getS3AdminsFromConfig(configuration))
.containsAll(Arrays.asList("alice", "bob"));
}

@Test
void testS3AdminExtractionWithFallback() throws IOException {
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS, "alice,bob");

assertThat(OzoneAdmins.getS3AdminsFromConfig(configuration))
.containsAll(Arrays.asList("alice", "bob"));
}

@Test
void testS3AdminGroupExtraction() {
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS_GROUPS,
"test1, test2");
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved

assertThat(OzoneAdmins.getS3AdminsGroupsFromConfig(configuration))
.containsAll(Arrays.asList("test1", "test2"));
}

@Test
void testS3AdminGroupExtractionWithFallback() {
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS,
"test1, test2");
devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved

assertThat(OzoneAdmins.getS3AdminsGroupsFromConfig(configuration))
.containsAll(Arrays.asList("test1", "test2"));
}

@Test
void testGetS3AdminsWhenS3AdminPresent() {
// When there is S3 admin present
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS, "alice");
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS_GROUPS, "test_group");

OzoneAdmins admins = OzoneAdmins.getS3Admins(configuration);
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
"alice", new String[] {"test_group"});

assertThat(admins.isAdmin(ugi)).isEqualTo(true);

// Test that when a user is present in an admin group but not an Ozone Admin
UserGroupInformation ugiGroupOnly = UserGroupInformation.createUserForTesting(
"bob", new String[] {"test_group"});
assertThat(admins.isAdmin(ugiGroupOnly)).isEqualTo(true);
}
@Test
void testGetS3AdminsWhenNoS3AdminPresent() {
// When there is no S3 admin, but Ozone admins present
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS, "alice");
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS, "test_group");

OzoneAdmins admins = OzoneAdmins.getS3Admins(configuration);
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
"alice", new String[] {"test_group"});

assertThat(admins.isAdmin(ugi)).isEqualTo(true);

// Test that when a user is present in an admin group but not an Ozone Admin
UserGroupInformation ugiGroupOnly = UserGroupInformation.createUserForTesting(
"bob", new String[] {"test_group"});
assertThat(admins.isAdmin(ugiGroupOnly)).isEqualTo(true);
}

@Test
void testGetS3AdminsWithNoAdmins() {
OzoneAdmins admins = OzoneAdmins.getS3Admins(new OzoneConfiguration());
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
"alice", new String[] {"test_group"});
assertThat(admins.isAdmin(ugi)).isEqualTo(false);
}

@Test
void testIsS3AdminForS3AdminUser() {
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS, "alice");
configuration.set(OzoneConfigKeys.OZONE_S3_ADMINISTRATORS_GROUPS, "test_group");

UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
"alice", new String[] {"test_group"});
// Scenario when user is present in an admin group but not an Ozone Admin
UserGroupInformation ugiGroupOnly = UserGroupInformation.createUserForTesting(
"bob", new String[] {"test_group"});

assertThat(OzoneAdmins.isS3Admin(ugi, configuration)).isEqualTo(true);
assertThat(OzoneAdmins.isS3Admin(ugiGroupOnly, configuration)).isEqualTo(true);
}

@Test
void testIsS3AdminForAdminUser() {
// When there is no S3 admin, but Ozone admins present
OzoneConfiguration configuration = new OzoneConfiguration();
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS, "alice");
configuration.set(OzoneConfigKeys.OZONE_ADMINISTRATORS_GROUPS, "test_group");

OzoneAdmins admins = OzoneAdmins.getS3Admins(configuration);
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
"alice", new String[] {"test_group"});
// Test that when a user is present in an admin group but not an Ozone Admin
UserGroupInformation ugiGroupOnly = UserGroupInformation.createUserForTesting(
"bob", new String[] {"test_group"});

assertThat(admins.isAdmin(ugi)).isEqualTo(true);
assertThat(admins.isAdmin(ugiGroupOnly)).isEqualTo(true);
}

@Test
void testIsS3AdminForNoUser() {
OzoneConfiguration configuration = new OzoneConfiguration();
assertThat(OzoneAdmins.isS3Admin(null, configuration)).isEqualTo(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
package org.apache.hadoop.ozone.om.ha;

import io.grpc.Status;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.conf.ConfigurationException;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.utils.LegacyHadoopConfigurationSource;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneConsts;
Expand All @@ -41,6 +38,7 @@
import java.util.Optional;
import java.util.OptionalInt;
import io.grpc.StatusRuntimeException;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -59,10 +57,12 @@ public class GrpcOMFailoverProxyProvider<T> extends
public static final Logger LOG =
LoggerFactory.getLogger(GrpcOMFailoverProxyProvider.class);


devabhishekpal marked this conversation as resolved.
Show resolved Hide resolved
public GrpcOMFailoverProxyProvider(ConfigurationSource configuration,
UserGroupInformation ugi,
String omServiceId,
Class<T> protocol) throws IOException {
super(configuration, omServiceId, protocol);
super(configuration, ugi, omServiceId, protocol);
}

@Override
Expand Down Expand Up @@ -116,9 +116,7 @@ protected void loadOMClientConfigs(ConfigurationSource config, String omSvcId)

private T createOMProxy() throws IOException {
InetSocketAddress addr = new InetSocketAddress(0);
Configuration hadoopConf =
LegacyHadoopConfigurationSource.asHadoopConfiguration(getConf());
return (T) RPC.getProxy(getInterface(), 0, addr, hadoopConf);
return createOMProxy(addr);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,9 @@
import java.util.List;
import java.util.Map;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.utils.LegacyHadoopConfigurationSource;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.retry.RetryPolicies;
import org.apache.hadoop.io.retry.RetryPolicy;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.ha.ConfUtils;
Expand All @@ -59,9 +53,7 @@ public class HadoopRpcOMFailoverProxyProvider<T> extends
public static final Logger LOG =
LoggerFactory.getLogger(HadoopRpcOMFailoverProxyProvider.class);

private final long omVersion;
private final Text delegationTokenService;
private final UserGroupInformation ugi;
private Map<String, OMProxyInfo> omProxyInfos;
private List<String> retryExceptions = new ArrayList<>();

Expand All @@ -75,9 +67,7 @@ public HadoopRpcOMFailoverProxyProvider(ConfigurationSource configuration,
UserGroupInformation ugi,
String omServiceId,
Class<T> protocol) throws IOException {
super(configuration, omServiceId, protocol);
this.ugi = ugi;
this.omVersion = RPC.getProtocolVersion(protocol);
super(configuration, ugi, omServiceId, protocol);
this.delegationTokenService = computeDelegationTokenService();
}

Expand Down Expand Up @@ -130,24 +120,6 @@ protected void loadOMClientConfigs(ConfigurationSource config, String omSvcId)
setOmNodeAddressMap(omNodeAddressMap);
}

private T createOMProxy(InetSocketAddress omAddress) throws IOException {
Configuration hadoopConf =
LegacyHadoopConfigurationSource.asHadoopConfiguration(getConf());
RPC.setProtocolEngine(hadoopConf, getInterface(), ProtobufRpcEngine.class);

// FailoverOnNetworkException ensures that the IPC layer does not attempt
// retries on the same OM in case of connection exception. This retry
// policy essentially results in TRY_ONCE_THEN_FAIL.
RetryPolicy connectionRetryPolicy = RetryPolicies
.failoverOnNetworkException(0);

return (T) RPC.getProtocolProxy(getInterface(), omVersion,
omAddress, ugi, hadoopConf, NetUtils.getDefaultSocketFactory(
hadoopConf), (int) OmUtils.getOMClientRpcTimeOut(getConf()),
connectionRetryPolicy).getProxy();

}

/**
* Get the proxy object which should be used until the next failover event
* occurs. RPC proxy object is intialized lazily.
Expand Down
Loading