[ https://issues.apache.org/jira/browse/KNOX-3048?focusedWorklogId=972301&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-972301 ]
ASF GitHub Bot logged work on KNOX-3048: ---------------------------------------- Author: ASF GitHub Bot Created on: 09/Jun/25 20:27 Start Date: 09/Jun/25 20:27 Worklog Time Spent: 10m Work Description: pzampino commented on code in PR #1043: URL: https://github.com/apache/knox/pull/1043#discussion_r2136451154 ########## gateway-spi/src/main/java/org/apache/knox/gateway/util/GroupBasedImpersonationProvider.java: ########## @@ -0,0 +1,257 @@ +/* + * 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.knox.gateway.util; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AccessControlList; +import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.hadoop.security.authorize.DefaultImpersonationProvider; +import org.apache.hadoop.util.MachineList; +import org.apache.knox.gateway.i18n.GatewaySpiMessages; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; + +import java.net.InetAddress; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYGROUP_PREFIX; +import static org.apache.knox.gateway.util.AuthFilterUtils.PROXYUSER_PREFIX; + +/** + * An extension of Hadoop's DefaultImpersonationProvider that adds support for group-based impersonation. + * This provider allows users who belong to specific groups to impersonate other users. + */ +public class GroupBasedImpersonationProvider extends DefaultImpersonationProvider { + private static final GatewaySpiMessages LOG = MessagesFactory.get(GatewaySpiMessages.class); + private static final String CONF_HOSTS = ".hosts"; + private static final String CONF_USERS = ".users"; + private static final String CONF_GROUPS = ".groups"; + private final Map<String, AccessControlList> proxyGroupsAcls = new HashMap<>(); + private Map<String, MachineList> groupProxyHosts = new HashMap<>(); + private String groupConfigPrefix; + private boolean doesProxyUserConfigExist = true; + static final String IMPERSONATION_ENABLED_PARAM = "impersonation.enabled"; + + public GroupBasedImpersonationProvider() { + super(); + } + + @Override + public Configuration getConf() { + return super.getConf(); + } + + @Override + public void setConf(Configuration conf) { + super.setConf(conf); + } + + @Override + public void init(String configurationPrefix) { + super.init(configurationPrefix); + + /* Check if user proxy configs are provided */ + final Map<String, String> filteredProps = Optional.ofNullable(getConf().getPropsWithPrefix(PROXYUSER_PREFIX + ".")) + .orElse(Collections.emptyMap()) // handle null map defensively + .entrySet() + .stream() + .filter(entry -> !IMPERSONATION_ENABLED_PARAM.equals(entry.getKey())) // avoid NPE by reversing equals + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + doesProxyUserConfigExist = !filteredProps.isEmpty(); + + initGroupBasedProvider(PROXYGROUP_PREFIX); + } + + private void initGroupBasedProvider(String proxyGroupPrefix) { + groupConfigPrefix = proxyGroupPrefix + + (proxyGroupPrefix.endsWith(".") ? "" : "."); + + // constructing regex to match the following patterns: + // $configPrefix.[ANY].users + // $configPrefix.[ANY].groups + // $configPrefix.[ANY].hosts + // + String prefixRegEx = groupConfigPrefix.replace(".", "\\."); + String usersGroupsRegEx = prefixRegEx + "[\\S]*(" + + Pattern.quote(CONF_USERS) + "|" + Pattern.quote(CONF_GROUPS) + ")"; + String hostsRegEx = prefixRegEx + "[\\S]*" + Pattern.quote(CONF_HOSTS); + + // get list of users and groups per proxygroup + // Map of <hadoop.proxygroup.[VIRTUAL_GROUP].users|groups, group1,group2> + Map<String, String> allMatchKeys = + getConf().getValByRegex(usersGroupsRegEx); + + for (Map.Entry<String, String> entry : allMatchKeys.entrySet()) { + //aclKey = hadoop.proxygroup.[VIRTUAL_GROUP] + String aclKey = getAclKey(entry.getKey()); + + if (!proxyGroupsAcls.containsKey(aclKey)) { + proxyGroupsAcls.put(aclKey, new AccessControlList( + allMatchKeys.get(aclKey + CONF_USERS), + allMatchKeys.get(aclKey + CONF_GROUPS))); + } + } + + // get hosts per proxygroup + allMatchKeys = getConf().getValByRegex(hostsRegEx); + for (Map.Entry<String, String> entry : allMatchKeys.entrySet()) { + groupProxyHosts.put(entry.getKey(), + new MachineList(entry.getValue())); + } + } + + private String getAclKey(String key) { + int endIndex = key.lastIndexOf('.'); + if (endIndex != -1) { + return key.substring(0, endIndex); + } + return key; + } + + /** + * Authorization based on user and group impersonation policies. + * + * @param user the user information attempting the operation, which includes the real + * user and the effective impersonated user. + * @param remoteAddress the remote address from which the user is connecting. + * @throws AuthorizationException if the user is not authorized based on the + * configured impersonation and group policies. + */ + @Override + public void authorize(UserGroupInformation user, InetAddress remoteAddress) throws AuthorizationException { + authorize(user, remoteAddress, Collections.emptyList()); + } + + /** + * Authorization based on groups that are provided as a function argument + * + * @param user the user information attempting the operation, which includes the real + * user and the effective impersonated user. + * @param groups the list of groups to check for authorization. + * @param remoteAddress the remote address from which the user is connecting. + * @throws AuthorizationException if the user is not authorized based on the + * configured impersonation and group policies. + */ + public void authorize(UserGroupInformation user, InetAddress remoteAddress, List<String> groups) throws AuthorizationException { + + /** + * check for proxy user authorization only if PROXYUSER_PREFIX properties exist. + * If proxy is configured then use those properties instead of group-based properties + * given user based configs are more finegrained. + */ + if (doesProxyUserConfigExist) { + super.authorize(user, remoteAddress); + } else{ Review Comment: I mean that the presence of proxyuser config should not preclude the evaluation of the group-level config. If there is user-level config that does not apply to the requesting user (super.authorize() fails), then the group-level config should be evaluated to see if there is matching authorization based on the requesting user's group membership. Issue Time Tracking ------------------- Worklog Id: (was: 972301) Time Spent: 4h (was: 3h 50m) > Surrogate proxy user configuration for user groups > -------------------------------------------------- > > Key: KNOX-3048 > URL: https://issues.apache.org/jira/browse/KNOX-3048 > Project: Apache Knox > Issue Type: Improvement > Components: Server > Affects Versions: 2.0.0 > Reporter: Philip Zampino > Assignee: Sandeep More > Priority: Major > Fix For: 2.1.0 > > Time Spent: 4h > Remaining Estimate: 0h > > *Problem Statement:* > Currently Knox has the ability for specific users (say for e.g. {{sp_user}}) > to impersonate other users (say for e.g.{{ot_user}}). This is driven by > configs defined in a topology. Currently these configs are needed for each > user that impersonates other users (i.e. {{sp_user}}), this can get out of > hand quickly and can be difficult to maintain. > To solve this problem the proposed solution uses a group level impersonation > configuration. This configuration will be based on the virtual groups feature > that is already available in Knox. With this new configuration we can have > specific users who belong to a virtual group/s (based on conditions such as > {{(match groups 'analyst|scientist') }}) impersonate other users. This will > significantly cut down on the config properties and keep them readable and > maintainable. -- This message was sent by Atlassian Jira (v8.20.10#820010)