[ https://issues.apache.org/jira/browse/KNOX-3048?focusedWorklogId=969994&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-969994 ]
ASF GitHub Bot logged work on KNOX-3048: ---------------------------------------- Author: ASF GitHub Bot Created on: 20/May/25 12:38 Start Date: 20/May/25 12:38 Worklog Time Spent: 10m Work Description: zeroflag commented on code in PR #1043: URL: https://github.com/apache/knox/pull/1043#discussion_r2097847028 ########## gateway-spi/src/main/java/org/apache/knox/gateway/util/GroupBasedImpersonationProvider.java: ########## @@ -0,0 +1,252 @@ +/* + * 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.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.apache.knox.gateway.util.AuthFilterUtils.DEFAULT_IMPERSONATION_MODE; +import static org.apache.knox.gateway.util.AuthFilterUtils.IMPERSONATION_MODE; + +/** + * 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<>(); + /* is the proxy user configuration enabled */ + boolean isProxyUserEnabled = true; + /* is the proxy group configuration enabled, defaulting it to false for backwards compatibility */ + boolean isProxyGroupEnabled; + private Map<String, MachineList> groupProxyHosts = new HashMap<>(); + private String groupConfigPrefix; + private String impersonationMode = DEFAULT_IMPERSONATION_MODE; + + public GroupBasedImpersonationProvider() { + super(); + } + + public GroupBasedImpersonationProvider(boolean isProxyUserEnabled, boolean isProxyGroupEnabled) { + super(); + this.isProxyUserEnabled = isProxyUserEnabled; + this.isProxyGroupEnabled = isProxyGroupEnabled; + } + + @Override + public Configuration getConf() { + return super.getConf(); + } + + @Override + public void setConf(Configuration conf) { + super.setConf(conf); + } + + public void init(String configurationPrefix, String proxyGroupPrefix) { + super.init(configurationPrefix); + initGroupBasedProvider(proxyGroupPrefix); + } + + private void initGroupBasedProvider(String proxyGroupPrefix) { + impersonationMode = getConf().get(IMPERSONATION_MODE, DEFAULT_IMPERSONATION_MODE); + 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; + } + + @Override + public void authorize(UserGroupInformation user, InetAddress remoteAddress) throws AuthorizationException { + // If both authorization methods are disabled, allow the operation to proceed + if (!isProxyUserEnabled && !isProxyGroupEnabled) { + LOG.successfulImpersonation(user.getRealUser().getUserName(), user.getUserName()); + return; + } + + boolean isProxyUserAuthorized = false; + boolean isProxyGroupAuthorized = false; + if (isProxyUserEnabled) { + /* authorize proxy user */ + isProxyUserAuthorized = checkProxyUserAuthorization(user, remoteAddress); + /* In case of AND no reason to check for proxy groups if isProxyUserAuthorized=false */ + if("AND".equalsIgnoreCase(impersonationMode) && !isProxyUserAuthorized) { Review Comment: I would either extract this (`"AND"`) to a string constant, or introduce an enum. Issue Time Tracking ------------------- Worklog Id: (was: 969994) Time Spent: 1.5h (was: 1h 20m) > 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: 1.5h > 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)