blob: de397d031fd23d821318d6cb0c79f297fcfb0f88 [file] [log] [blame]
/**
* 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.ozone.om;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import com.google.common.base.Optional;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.annotation.InterfaceStability;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.multitenant.AuthorizerLock;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessController.Acl;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessController.Policy;
import org.apache.hadoop.ozone.om.multitenant.OMRangerBGSyncService;
import org.apache.hadoop.ozone.om.multitenant.OzoneOwnerPrincipal;
import org.apache.hadoop.ozone.om.multitenant.Tenant;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.slf4j.Logger;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_TENANT_RANGER_POLICY_LABEL;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_KEYTAB_FILE_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED_DEFAULT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_SERVICE;
import static org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.CREATE;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.LIST;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
/**
* OM MultiTenant manager interface.
*/
@InterfaceAudience.Private
@InterfaceStability.Unstable
public interface OMMultiTenantManager {
/* TODO: Outdated
* Init multi-tenant manager. Performs initialization e.g.
* - Initialize Multi-Tenant-Gatekeeper-Plugin
* - Validate Multi-Tenant Bucket-NameSpaces
* - Validate Multi-Tenant Account-NameSpaces
* - Validating various OM (Multi-Tenant state)tables and corresponding
* state in IMultiTenantGateKeeperPlugin (Ranger/Native/AnyOtherPlugIn).
* - Setup SuperUsers for Multi-Tenant environment from Ozone-Conf
* - Periodic BackGround thread to keep MultiTenant-State consistent e.g.
* . superusers <-in-sync-> OzoneConf,
* . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
* . OM DB state is always the source of truth.
*/
/**
* Start background thread(s) in the multi-tenant manager.
*/
void start() throws IOException;
/**
* Stop background thread(s) in the multi-tenant manager.
*/
void stop() throws IOException;
/**
* Returns the instance of OMRangerBGSyncService.
*/
OMRangerBGSyncService getOMRangerBGSyncService();
/**
* Returns the corresponding OzoneManager instance.
*
* @return OMMetadataManager
*/
OMMetadataManager getOmMetadataManager();
TenantOp getAuthorizerOp();
TenantOp getCacheOp();
/**
* Given an accessId, return kerberos user name for the tenant user.
*/
String getUserNameGivenAccessId(String accessId);
/**
* Get the default Access ID string given tenant name and user name.
* @param tenantId tenant name
* @param userPrincipal user name
* @return access ID in the form of tenantName$username
*/
static String getDefaultAccessId(String tenantId, String userPrincipal) {
return tenantId + OzoneConsts.TENANT_ID_USERNAME_DELIMITER + userPrincipal;
}
/**
* Returns true if user is the tenant's admin or Ozone admin, false otherwise.
* @param callerUgi caller's UserGroupInformation
* @param tenantId tenant name
* @param delegated if set to true, checks if the user is a delegated tenant
* admin; if set to false, checks if the user is a tenant
* admin, delegated or not
*/
boolean isTenantAdmin(UserGroupInformation callerUgi, String tenantId,
boolean delegated);
/**
* List all the user & accessIDs of all users that belong to this Tenant.
* Note this read is unprotected. See OzoneManager#listUserInTenant
* @param tenantID
* @return List of users
*/
TenantUserList listUsersInTenant(String tenantID, String prefix)
throws IOException;
/**
* Given an access ID return its corresponding tenant.
* @param accessID
* @return String tenant name
*/
Optional<String> getTenantForAccessID(String accessID) throws IOException;
/**
* Get default user role name given tenant name.
* @param tenantId tenant name
* @return user role name. e.g. tenant1-UserRole
*/
static String getDefaultUserRoleName(String tenantId) {
return tenantId + OzoneConsts.DEFAULT_TENANT_ROLE_USER_SUFFIX;
}
/**
* Get default admin role name given tenant name.
* @param tenantId tenant name
* @return admin role name. e.g. tenant1-AdminRole
*/
static String getDefaultAdminRoleName(String tenantId) {
return tenantId + OzoneConsts.DEFAULT_TENANT_ROLE_ADMIN_SUFFIX;
}
/**
* Get default bucket namespace (volume) policy name given tenant name.
* @param tenantId tenant name
* @return bucket namespace (volume) policy name. e.g. tenant1-VolumeAccess
*/
static String getDefaultBucketNamespacePolicyName(String tenantId) {
return tenantId + OzoneConsts.DEFAULT_TENANT_BUCKET_NAMESPACE_POLICY_SUFFIX;
}
/**
* Get default bucket policy name given tenant name.
* @param tenantId tenant name
* @return bucket policy name. e.g. tenant1-BucketAccess
*/
static String getDefaultBucketPolicyName(String tenantId) {
return tenantId + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
}
/**
* Passes check only when caller is an Ozone (cluster) admin, throws
* OMException otherwise.
* @throws OMException PERMISSION_DENIED
*/
void checkAdmin() throws OMException;
/**
* Check if caller is a tenant admin of the specified tenant.
* Ozone admins will always pass this check.
* Throws PERMISSION_DENIED if the check failed.
* @param tenantId tenant name
* @param delegated if set to true, only delegated tenant admins can pass this
* check; if false, both delegated and non-delegated tenant
* admins will pass this check.
* @throws OMException PERMISSION_DENIED
*/
void checkTenantAdmin(String tenantId, boolean delegated) throws OMException;
/**
* Check if the tenantId exists in the table, throws TENANT_NOT_FOUND if not.
*/
void checkTenantExistence(String tenantId) throws OMException;
/**
* Retrieve volume name of the tenant.
*
* Throws OMException TENANT_NOT_FOUND if tenantId doesn't exist.
*/
String getTenantVolumeName(String tenantId) throws IOException;
/**
* Retrieve user role name of the given tenant.
* @param tenantId tenant name
* @return tenant user role name
*/
String getTenantUserRoleName(String tenantId) throws IOException;
/**
* Retrieve admin role name of the given tenant.
* @param tenantId tenant name
* @return tenant user role name
*/
String getTenantAdminRoleName(String tenantId) throws IOException;
/**
* Get Tenant object of given tenant name from OM DB.
* @param tenantId tenant name
* @return Tenant
* @throws IOException
*/
Tenant getTenantFromDBById(String tenantId) throws IOException;
boolean isUserAccessIdPrincipalOrTenantAdmin(String accessId,
UserGroupInformation ugi) throws IOException;
/**
* Returns true if the tenant doesn't have any accessIds assigned to it
* Returns false otherwise.
*
* @param tenantId
* @throws IOException
*/
boolean isTenantEmpty(String tenantId) throws IOException;
/**
* Returns true if Multi-Tenancy can be successfully enabled given the OM
* instance and conf; returns false if ozone.om.multitenancy.enabled = false
*
* Config validation will be performed on conf if the intent to enable
* Multi-Tenancy is specified (i.e. ozone.om.multitenancy.enabled = true),
* if the validation failed, an exception will be thrown to prevent OM from
* starting up.
*/
static boolean checkAndEnableMultiTenancy(
OzoneManager ozoneManager, OzoneConfiguration conf) {
// Borrow the logger from OM instance
final Logger logger = OzoneManager.LOG;
boolean isS3MultiTenancyEnabled = conf.getBoolean(
OZONE_OM_MULTITENANCY_ENABLED, OZONE_OM_MULTITENANCY_ENABLED_DEFAULT);
final boolean devSkipMTCheck = conf.getBoolean(
OZONE_OM_TENANT_DEV_SKIP_RANGER, false);
// If ozone.om.multitenancy.enabled = false, skip the validation
// Or if dev skip check flag is set, skip the validation (used in UT)
if (!isS3MultiTenancyEnabled || devSkipMTCheck) {
return isS3MultiTenancyEnabled;
}
// Validate configs required to enable S3 multi-tenancy
if (!ozoneManager.isSecurityEnabled()) {
isS3MultiTenancyEnabled = false;
logger.error("Ozone security is required to enable S3 Multi-Tenancy");
} else if (!SecurityUtil.getAuthenticationMethod(conf).equals(
AuthenticationMethod.KERBEROS)) {
isS3MultiTenancyEnabled = false;
logger.error("Kerberos authentication is required to enable S3 "
+ "Multi-Tenancy");
}
// TODO: Validate accessAuthorizer later. We can't do that for now:
// 1. Tenant acceptance test env (ozonesecure) uses OzoneNativeAuthorizer
// 2. RangerOzoneAuthorizer is external class
final String rangerAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);
if (StringUtils.isBlank(rangerAddress)) {
isS3MultiTenancyEnabled = false;
logger.error("{} is required to enable S3 Multi-Tenancy but not set",
OZONE_RANGER_HTTPS_ADDRESS_KEY);
}
final String rangerService = conf.get(OZONE_RANGER_SERVICE);
if (StringUtils.isBlank(rangerService)) {
isS3MultiTenancyEnabled = false;
logger.error("{} is required to enable S3 Multi-Tenancy but not set",
OZONE_RANGER_SERVICE);
}
String fallbackUsername = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
String fallbackPassword = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
if (fallbackUsername != null && fallbackPassword != null) {
logger.warn("Detected clear text username and password override configs. "
+ "These will be used to authenticate to Ranger Admin Server instead "
+ "of using the recommended Kerberos principal and keytab "
+ "authentication method. "
+ "This is NOT recommended on a production cluster.");
} else {
// Check Kerberos principal and keytab file path configs if not both
// clear text username and password overrides are set.
final String omKerbPrinc = conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY);
// Note: ozone.om.kerberos.keytab.file and ozone.om.kerberos.principal
// are not empty by default. The default values may or may not be valid.
if (StringUtils.isBlank(omKerbPrinc)) {
isS3MultiTenancyEnabled = false;
logger.error("{} is required to enable S3 Multi-Tenancy but not set",
OZONE_OM_KERBEROS_PRINCIPAL_KEY);
}
final String rangerPw = conf.get(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY);
if (StringUtils.isBlank(rangerPw)) {
isS3MultiTenancyEnabled = false;
logger.error("{} is required to enable S3 Multi-Tenancy but not set",
OZONE_OM_KERBEROS_KEYTAB_FILE_KEY);
}
if (!(new File(rangerPw).isFile())) {
logger.error("{} = '{}' file path doesn't exist or is not a file",
OZONE_OM_KERBEROS_KEYTAB_FILE_KEY, rangerPw);
}
}
if (!isS3MultiTenancyEnabled) {
throw new RuntimeException("Failed to meet one or more requirements to "
+ "enable S3 Multi-Tenancy");
}
return true;
}
String OZONE_TENANT_RANGER_POLICY_DESCRIPTION =
"Created by Ozone. WARNING: "
+ "Changes will be lost when this tenant is deleted.";
String OZONE_TENANT_RANGER_ROLE_DESCRIPTION =
"Managed by Ozone. WARNING: "
+ "Changes will be overridden. "
+ "Use Ozone tenant CLI to manage users in this tenant role instead.";
/**
* Returns default VolumeAccess policy given tenant and role names.
*/
static Policy getDefaultVolumeAccessPolicy(
String tenantId, String volumeName,
String userRoleName, String adminRoleName)
throws IOException {
final String volumePolicyName = OMMultiTenantManager
.getDefaultBucketNamespacePolicyName(tenantId);
return new Policy.Builder()
.setName(volumePolicyName)
.addVolume(volumeName)
.setDescription(OZONE_TENANT_RANGER_POLICY_DESCRIPTION)
.addLabel(OZONE_TENANT_RANGER_POLICY_LABEL)
.addRoleAcl(userRoleName, Arrays.asList(
Acl.allow(READ), Acl.allow(LIST), Acl.allow(READ_ACL)))
.addRoleAcl(adminRoleName, Collections.singletonList(Acl.allow(ALL)))
.build();
}
/**
* Returns default BucketAccess policy given tenant and user role name.
*/
static Policy getDefaultBucketAccessPolicy(
String tenantId, String volumeName,
String userRoleName) throws IOException {
final String bucketPolicyName = OMMultiTenantManager
.getDefaultBucketPolicyName(tenantId);
return new Policy.Builder()
.setName(bucketPolicyName)
.addVolume(volumeName)
.addBucket("*")
.setDescription(OZONE_TENANT_RANGER_POLICY_DESCRIPTION)
.addLabel(OZONE_TENANT_RANGER_POLICY_LABEL)
.addRoleAcl(userRoleName,
Collections.singletonList(Acl.allow(CREATE)))
.addUserAcl(new OzoneOwnerPrincipal().getName(),
Collections.singletonList(Acl.allow(ALL)))
.build();
}
AuthorizerLock getAuthorizerLock();
}