001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.token; 019 020import java.io.IOException; 021import java.util.Collections; 022import org.apache.hadoop.hbase.CoprocessorEnvironment; 023import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor; 024import org.apache.hadoop.hbase.coprocessor.HasRegionServerServices; 025import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 026import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 027import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; 028import org.apache.hadoop.hbase.ipc.RpcServer; 029import org.apache.hadoop.hbase.ipc.RpcServerInterface; 030import org.apache.hadoop.hbase.regionserver.RegionServerServices; 031import org.apache.hadoop.hbase.security.AccessDeniedException; 032import org.apache.hadoop.hbase.security.User; 033import org.apache.hadoop.security.UserGroupInformation; 034import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 035import org.apache.hadoop.security.token.SecretManager; 036import org.apache.hadoop.security.token.Token; 037import org.apache.yetus.audience.InterfaceAudience; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import org.apache.hbase.thirdparty.com.google.protobuf.RpcCallback; 042import org.apache.hbase.thirdparty.com.google.protobuf.RpcController; 043import org.apache.hbase.thirdparty.com.google.protobuf.Service; 044 045import org.apache.hadoop.hbase.shaded.protobuf.generated.AuthenticationProtos; 046 047/** 048 * Provides a service for obtaining authentication tokens via the {@link AuthenticationProtos} 049 * AuthenticationService coprocessor service. 050 */ 051@CoreCoprocessor 052@InterfaceAudience.Private 053public class TokenProvider 054 implements AuthenticationProtos.AuthenticationService.Interface, RegionCoprocessor { 055 056 private static final Logger LOG = LoggerFactory.getLogger(TokenProvider.class); 057 058 private AuthenticationTokenSecretManager secretManager; 059 060 @Override 061 public void start(CoprocessorEnvironment env) { 062 // if running at region 063 if (env instanceof RegionCoprocessorEnvironment) { 064 RegionCoprocessorEnvironment regionEnv = (RegionCoprocessorEnvironment) env; 065 /* 066 * Getting the RpcServer from a RegionCE is wrong. There cannot be an expectation that Region 067 * is hosted inside a RegionServer. If you need RpcServer, then pass in a RegionServerCE. 068 * TODO: FIX. 069 */ 070 RegionServerServices rss = ((HasRegionServerServices) regionEnv).getRegionServerServices(); 071 RpcServerInterface server = rss.getRpcServer(); 072 SecretManager<?> mgr = ((RpcServer) server).getSecretManager(); 073 if (mgr instanceof AuthenticationTokenSecretManager) { 074 secretManager = (AuthenticationTokenSecretManager) mgr; 075 } 076 } 077 } 078 079 @Override 080 public void stop(CoprocessorEnvironment env) throws IOException { 081 } 082 083 /** 084 * @param ugi A user group information. 085 * @return true if delegation token operation is allowed 086 */ 087 private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException { 088 AuthenticationMethod authMethod = ugi.getAuthenticationMethod(); 089 if (authMethod == AuthenticationMethod.PROXY) { 090 authMethod = ugi.getRealUser().getAuthenticationMethod(); 091 } 092 if ( 093 authMethod != AuthenticationMethod.KERBEROS && authMethod != AuthenticationMethod.KERBEROS_SSL 094 && authMethod != AuthenticationMethod.CERTIFICATE 095 ) { 096 return false; 097 } 098 return true; 099 } 100 101 // AuthenticationService implementation 102 103 @Override 104 public Iterable<Service> getServices() { 105 return Collections 106 .singleton(AuthenticationProtos.AuthenticationService.newReflectiveService(this)); 107 } 108 109 @Override 110 public void getAuthenticationToken(RpcController controller, 111 AuthenticationProtos.GetAuthenticationTokenRequest request, 112 RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) { 113 AuthenticationProtos.GetAuthenticationTokenResponse.Builder response = 114 AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder(); 115 116 try { 117 if (secretManager == null) { 118 throw new IOException("No secret manager configured for token authentication"); 119 } 120 User currentUser = RpcServer.getRequestUser() 121 .orElseThrow(() -> new AccessDeniedException("No authenticated user for request!")); 122 UserGroupInformation ugi = currentUser.getUGI(); 123 if (!isAllowedDelegationTokenOp(ugi)) { 124 LOG.warn("Token generation denied for user=" + currentUser.getName() + ", authMethod=" 125 + ugi.getAuthenticationMethod()); 126 throw new AccessDeniedException( 127 "Token generation only allowed for Kerberos authenticated clients"); 128 } 129 130 Token<AuthenticationTokenIdentifier> token = 131 secretManager.generateToken(currentUser.getName()); 132 response.setToken(ClientTokenUtil.toToken(token)).build(); 133 } catch (IOException ioe) { 134 CoprocessorRpcUtils.setControllerException(controller, ioe); 135 } 136 done.run(response.build()); 137 } 138 139 @Override 140 public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request, 141 RpcCallback<AuthenticationProtos.WhoAmIResponse> done) { 142 AuthenticationProtos.WhoAmIResponse.Builder response = 143 AuthenticationProtos.WhoAmIResponse.newBuilder(); 144 RpcServer.getRequestUser().ifPresent(requestUser -> { 145 response.setUsername(requestUser.getShortName()); 146 AuthenticationMethod method = requestUser.getUGI().getAuthenticationMethod(); 147 if (method != null) { 148 response.setAuthMethod(method.name()); 149 } 150 }); 151 done.run(response.build()); 152 } 153}