Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -43,6 +43,7 @@ public class RangerServiceDef extends RangerBaseModelObject implements java.io.S
public static final String OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES = "enableDenyAndExceptionsInPolicies";
public static final String OPTION_ENABLE_IMPLICIT_CONDITION_EXPRESSION = "enableImplicitConditionExpression";
public static final String OPTION_ENABLE_TAG_BASED_POLICIES = "enableTagBasedPolicies";
public static final String OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION = "enableActionMatcherInPoliciesCondition";
public static final String OPTION_RRN_RESOURCE_SEP_CHAR = "rrnResourceSepChar";

public static final char DEFAULT_RRN_RESOURCE_SEP_CHAR = '/';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@
import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
import org.apache.ranger.plugin.model.RangerInlinePolicy;
import org.apache.ranger.plugin.model.RangerPrincipal;
import org.apache.ranger.plugin.model.RangerServiceDef;
import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResult;
import org.apache.ranger.plugin.service.RangerBasePlugin;
import org.apache.ranger.plugin.util.JsonUtilsV2;
import org.apache.ranger.plugin.util.RangerPerfTracer;
import org.apache.ranger.plugin.util.ServiceDefUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -164,7 +166,11 @@ public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context) {

rangerRequest.setResource(rangerResource);
rangerRequest.setAccessType(accessType);
rangerRequest.setAction(context.getS3Action());
if (isOzoneActionPolicyEnabled(plugin)) {
rangerRequest.setAction(context.getS3Action());
} else {
rangerRequest.setAction(accessType);
}
rangerRequest.setRequestData(resource);
rangerRequest.setClusterName(clusterName);

Expand All @@ -185,7 +191,8 @@ public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context) {

try {
if (StringUtils.isNotBlank(context.getSessionPolicy())) {
rangerRequest.setInlinePolicy(JsonUtilsV2.jsonToObj(context.getSessionPolicy(), RangerInlinePolicy.class));
RangerInlinePolicy inlinePolicy = JsonUtilsV2.jsonToObj(context.getSessionPolicy(), RangerInlinePolicy.class);
rangerRequest.setInlinePolicy(sanitizeInlinePolicyForActionFlag(inlinePolicy, plugin));
}

RangerAccessResult result = plugin
Expand Down Expand Up @@ -302,9 +309,11 @@ private static RangerInlinePolicy.Grant toRangerGrant(OzoneGrant ozoneGrant, Ran
ret.setPermissions(ozoneGrant.getPermissions().stream().map(RangerOzoneAuthorizer::toRangerPermission).filter(Objects::nonNull).collect(Collectors.toSet()));
}

final Set<String> s3Actions = ozoneGrant.getS3Actions();
if (s3Actions != null && !s3Actions.isEmpty()) {
ret.setActions(s3Actions);
if (isOzoneActionPolicyEnabled(plugin)) {
final Set<String> s3Actions = ozoneGrant.getS3Actions();
if (s3Actions != null && !s3Actions.isEmpty()) {
ret.setActions(s3Actions);
}
}

LOG.debug("toRangerGrant(ozoneGrant={}): ret={}", ozoneGrant, ret);
Expand Down Expand Up @@ -374,5 +383,32 @@ private static String toRangerPermission(ACLType acl) {

return null;
}

private static boolean isOzoneActionPolicyEnabled(final RangerBasePlugin plugin) {
return plugin != null
&& plugin.getServiceDef() != null
&& ServiceDefUtil.getBooleanValue(
plugin.getServiceDef().getOptions(),
RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
false);
}

/**
* When action-policy is disabled, strip grant actions so generic inline evaluation
* does not enforce S3 actions (same effect as omitting actions in {@link #toRangerGrant}).
*/
static RangerInlinePolicy sanitizeInlinePolicyForActionFlag(RangerInlinePolicy policy, RangerBasePlugin plugin) {
if (policy == null || isOzoneActionPolicyEnabled(plugin) || policy.getGrants() == null) {
return policy;
}

for (RangerInlinePolicy.Grant grant : policy.getGrants()) {
if (grant != null) {
grant.setActions(null);
}
}

return policy;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
import org.apache.ranger.plugin.model.RangerInlinePolicy;
import org.apache.ranger.plugin.model.RangerServiceDef;
import org.apache.ranger.plugin.service.RangerBasePlugin;
import org.apache.ranger.plugin.util.JsonUtilsV2;
import org.junit.jupiter.api.BeforeAll;
Expand All @@ -37,7 +38,9 @@
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -55,6 +58,7 @@ public class TestRangerOzoneAuthorizer {
private static final String OWNER_NAME = "ozone";

private static RangerOzoneAuthorizer ozoneAuthorizer;
private static RangerBasePlugin testPlugin;

private final String hostname = "localhost";
private final InetAddress ipAddress = InetAddress.getLoopbackAddress();
Expand Down Expand Up @@ -85,7 +89,9 @@ public static void setUpBeforeClass() {

// loads policies from om_dev_ozone.json, by EmbeddedResourcePolicySource configured in ranger-ozone-security.xml
plugin.init();
setOzoneActionPolicyEnabled(plugin);

testPlugin = plugin;
ozoneAuthorizer = new RangerOzoneAuthorizer(plugin);

assertNotNull(ozoneAuthorizer);
Expand Down Expand Up @@ -273,4 +279,126 @@ public void testCheckAccessWithS3Action() throws Exception {
assertFalse(ozoneAuthorizer.checkAccess(key1, ctxWriteNoAction),
"session-policy should deny write on key1 when no S3 action is specified and grant has S3 actions");
}

@Test
public void testAssumeRoleWithS3ActionsNotPropagatedWhenFlagDisabled() throws Exception {
final String previous = setOzoneActionPolicyDisabled();

try {
Set<OzoneGrant> grants = Collections.singleton(grantWriteWithS3Actions);
AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress, user1, role1, grants);

String sessionPolicy = ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);

assertNotNull(sessionPolicy);

RangerInlinePolicy grantPolicy = JsonUtilsV2.jsonToObj(sessionPolicy, RangerInlinePolicy.class);
RangerInlinePolicy.Grant grant = grantPolicy.getGrants().iterator().next();

assertNull(grant.getActions(), "S3 actions should not be propagated when ozone action policy is disabled");
} finally {
restoreOzoneActionPolicyEnabled(previous);
}
}

@Test
public void testCheckAccessIgnoresS3ActionWhenDisabled() throws Exception {
final String previous = setOzoneActionPolicyDisabled();

try {
Set<OzoneGrant> grants = Collections.singleton(grantWriteWithS3Actions);
AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress, user1, role1, grants);

String sessionPolicy = ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);

RequestContext ctxWriteGetObject = reqCtxBuilder
.setAclRights(IAccessAuthorizer.ACLType.WRITE)
.setRecursiveAccessCheck(false)
.setSessionPolicy(sessionPolicy)
.setS3Action("GetObject")
.build();

assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteGetObject),
"session-policy should allow write when S3 action enforcement is disabled");

RequestContext ctxWriteNoAction = reqCtxBuilder
.setAclRights(IAccessAuthorizer.ACLType.WRITE)
.setRecursiveAccessCheck(false)
.setSessionPolicy(sessionPolicy)
.setS3Action(null)
.build();

assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteNoAction),
"session-policy should allow write when S3 action enforcement is disabled");
} finally {
restoreOzoneActionPolicyEnabled(previous);
}
}

@Test
public void testLegacySessionPolicyActionsStrippedWhenFlagDisabled() throws Exception {
// Simulate a session policy issued when action-policy was enabled (actions embedded in JSON).
Set<OzoneGrant> grants = Collections.singleton(grantWriteWithS3Actions);
AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress, user1, role1, grants);

String legacySessionPolicy = ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);

RangerInlinePolicy legacyPolicy = JsonUtilsV2.jsonToObj(legacySessionPolicy, RangerInlinePolicy.class);
RangerInlinePolicy.Grant legacyGrant = legacyPolicy.getGrants().iterator().next();

assertNotNull(legacyGrant.getActions(),
"legacy session policy should contain S3 actions as if created with flag enabled");

final String previous = setOzoneActionPolicyDisabled();

try {
RangerInlinePolicy sanitized = RangerOzoneAuthorizer.sanitizeInlinePolicyForActionFlag(legacyPolicy, testPlugin);

assertNull(sanitized.getGrants().iterator().next().getActions(),
"sanitizeInlinePolicyForActionFlag should strip grant actions when flag is disabled");

RequestContext ctxWriteGetObject = reqCtxBuilder
.setAclRights(IAccessAuthorizer.ACLType.WRITE)
.setRecursiveAccessCheck(false)
.setSessionPolicy(legacySessionPolicy)
.setS3Action("GetObject")
.build();

assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteGetObject),
"legacy session-policy with embedded S3 actions should allow write when flag is disabled");
} finally {
restoreOzoneActionPolicyEnabled(previous);
}
}

private static void setOzoneActionPolicyEnabled(RangerBasePlugin plugin) {
if (plugin == null || plugin.getServiceDef() == null) {
return;
}

Map<String, String> options = plugin.getServiceDef().getOptions();

if (options == null) {
options = new HashMap<>();
plugin.getServiceDef().setOptions(options);
}

options.put(RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, Boolean.toString(true));
}

private static String setOzoneActionPolicyDisabled() {
Map<String, String> options = testPlugin.getServiceDef().getOptions();

return options.put(RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, Boolean.toString(false));
}

private static void restoreOzoneActionPolicyEnabled(String previous) {
Map<String, String> options = testPlugin.getServiceDef().getOptions();

if (previous == null) {
options.remove(RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION);
} else {
options.put(RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, previous);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.apache.commons.lang3.StringUtils;
import org.apache.ranger.biz.ServiceDBStore;
import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
import org.apache.ranger.common.JSONUtil;
import org.apache.ranger.common.RangerValidatorFactory;
import org.apache.ranger.db.RangerDaoManager;
Expand All @@ -27,18 +28,22 @@
import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator;
import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
import org.apache.ranger.service.RangerServiceDefService;
import org.apache.ranger.util.CLIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class PatchForOzoneServiceDefPolicyConditionUpdate_J10065 extends BaseLoader {
private static final Logger logger = LoggerFactory.getLogger(PatchForOzoneServiceDefPolicyConditionUpdate_J10065.class);
private static final String POLICY_CONDITION_ACTION_MATCHES = "action-matches";

@Autowired
RangerDaoManager daoMgr;
Expand Down Expand Up @@ -131,7 +136,21 @@ private void updateOzoneServiceDef() {
return;
}

dbOzoneServiceDef.setPolicyConditions(embeddedPolicyConditions);
final boolean enableActionMatcherInPoliciesCondition = RangerAdminConfig.getInstance().getBoolean(RangerServiceDefService.PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, false);
final List<RangerServiceDef.RangerPolicyConditionDef> updatedPolicyConditions;

if (enableActionMatcherInPoliciesCondition) {
updatedPolicyConditions = new ArrayList<>(embeddedPolicyConditions);
} else {
updatedPolicyConditions = new ArrayList<>();
for (RangerServiceDef.RangerPolicyConditionDef policyConditionDef : embeddedPolicyConditions) {
if (!StringUtils.equals(policyConditionDef.getName(), POLICY_CONDITION_ACTION_MATCHES)) {
updatedPolicyConditions.add(policyConditionDef);
}
}
}

dbOzoneServiceDef.setPolicyConditions(updatedPolicyConditions);

final RangerServiceDefValidator validator = validatorFactory.getServiceDefValidator(svcStore);

Expand All @@ -142,26 +161,48 @@ private void updateOzoneServiceDef() {
xXServiceDefObj = daoMgr.getXXServiceDef().findByName(ozoneServiceDefName);

if (xXServiceDefObj != null) {
final String jsonStrPostUpdate = xXServiceDefObj.getDefOptions();
Map<String, String> serviceDefOptionsPostUpdate = null;
updateOzoneServiceDefOptions(xXServiceDefObj, serviceDefOptionsPreUpdate, enableActionMatcherInPoliciesCondition);
}
} catch (Exception e) {
logger.error("Error while updating ozone service-def policy conditions", e);
throw new RuntimeException("Failed to update ozone service-def policy conditions", e);
}
}

if (StringUtils.isNotEmpty(jsonStrPostUpdate)) {
serviceDefOptionsPostUpdate = jsonUtil.jsonToMap(jsonStrPostUpdate);
}
private void updateOzoneServiceDefOptions(XXServiceDef xXServiceDefObj, Map<String, String> serviceDefOptionsPreUpdate,
boolean enableActionMatcherInPoliciesCondition) throws Exception {
final String previousJson = xXServiceDefObj.getDefOptions();
Map<String, String> options = parseDefOptions(previousJson);

if (serviceDefOptionsPostUpdate != null && serviceDefOptionsPostUpdate.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
if (serviceDefOptionsPreUpdate == null || !serviceDefOptionsPreUpdate.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
serviceDefOptionsPostUpdate.remove(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES);
if (options.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
if (serviceDefOptionsPreUpdate == null || !serviceDefOptionsPreUpdate.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
options.remove(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES);
}
}

xXServiceDefObj.setDefOptions(jsonUtil.readMapToString(serviceDefOptionsPostUpdate));
options.put(RangerServiceDef.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, Boolean.toString(enableActionMatcherInPoliciesCondition));

daoMgr.getXXServiceDef().update(xXServiceDefObj);
}
}
persistDefOptionsIfChanged(xXServiceDefObj, options, previousJson);
}

private Map<String, String> parseDefOptions(String jsonStr) throws Exception {
if (StringUtils.isNotEmpty(jsonStr)) {
Map<String, String> options = jsonUtil.jsonToMap(jsonStr);

if (options != null) {
return options;
}
} catch (Exception e) {
logger.error("Error while updating ozone service-def policy conditions", e);
throw new RuntimeException("Failed to update ozone service-def policy conditions", e);
}

return new HashMap<>();
}

private void persistDefOptionsIfChanged(XXServiceDef xServiceDef, Map<String, String> options, String previousJson) throws Exception {
String newJson = jsonUtil.readMapToString(options);

if (!StringUtils.equals(previousJson, newJson)) {
xServiceDef.setDefOptions(newJson);
daoMgr.getXXServiceDef().update(xServiceDef);
}
}
}
Loading
Loading