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 com.google.protobuf.RpcCallback; 021import com.google.protobuf.RpcController; 022import com.google.protobuf.Service; 023import java.io.IOException; 024import java.util.Collections; 025import org.apache.hadoop.hbase.CoprocessorEnvironment; 026import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor; 027import org.apache.hadoop.hbase.coprocessor.HasRegionServerServices; 028import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 029import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 030import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; 031import org.apache.hadoop.hbase.ipc.RpcServer; 032import org.apache.hadoop.hbase.ipc.RpcServerInterface; 033import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos; 034import org.apache.hadoop.hbase.regionserver.RegionServerServices; 035import org.apache.hadoop.hbase.security.AccessDeniedException; 036import org.apache.hadoop.hbase.security.User; 037import org.apache.hadoop.security.UserGroupInformation; 038import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 039import org.apache.hadoop.security.token.SecretManager; 040import org.apache.hadoop.security.token.Token; 041import org.apache.yetus.audience.InterfaceAudience; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045/** 046 * Provides a service for obtaining authentication tokens via the {@link AuthenticationProtos} 047 * AuthenticationService coprocessor service. 048 */ 049@CoreCoprocessor 050@InterfaceAudience.Private 051public class TokenProvider 052 implements AuthenticationProtos.AuthenticationService.Interface, RegionCoprocessor { 053 054 private static final Logger LOG = LoggerFactory.getLogger(TokenProvider.class); 055 056 private AuthenticationTokenSecretManager secretManager; 057 058 @Override 059 public void start(CoprocessorEnvironment env) { 060 // if running at region 061 if (env instanceof RegionCoprocessorEnvironment) { 062 RegionCoprocessorEnvironment regionEnv = (RegionCoprocessorEnvironment) env; 063 /* 064 * Getting the RpcServer from a RegionCE is wrong. There cannot be an expectation that Region 065 * is hosted inside a RegionServer. If you need RpcServer, then pass in a RegionServerCE. 066 * TODO: FIX. 067 */ 068 RegionServerServices rss = ((HasRegionServerServices) regionEnv).getRegionServerServices(); 069 RpcServerInterface server = rss.getRpcServer(); 070 SecretManager<?> mgr = ((RpcServer) server).getSecretManager(); 071 if (mgr instanceof AuthenticationTokenSecretManager) { 072 secretManager = (AuthenticationTokenSecretManager) mgr; 073 } 074 } 075 } 076 077 @Override 078 public void stop(CoprocessorEnvironment env) throws IOException { 079 } 080 081 /** 082 * @param ugi A user group information. 083 * @return true if delegation token operation is allowed 084 */ 085 private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException { 086 AuthenticationMethod authMethod = ugi.getAuthenticationMethod(); 087 if (authMethod == AuthenticationMethod.PROXY) { 088 authMethod = ugi.getRealUser().getAuthenticationMethod(); 089 } 090 if ( 091 authMethod != AuthenticationMethod.KERBEROS && authMethod != AuthenticationMethod.KERBEROS_SSL 092 && authMethod != AuthenticationMethod.CERTIFICATE 093 ) { 094 return false; 095 } 096 return true; 097 } 098 099 // AuthenticationService implementation 100 101 @Override 102 public Iterable<Service> getServices() { 103 return Collections 104 .singleton(AuthenticationProtos.AuthenticationService.newReflectiveService(this)); 105 } 106 107 @Override 108 public void getAuthenticationToken(RpcController controller, 109 AuthenticationProtos.GetAuthenticationTokenRequest request, 110 RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) { 111 AuthenticationProtos.GetAuthenticationTokenResponse.Builder response = 112 AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder(); 113 114 try { 115 if (secretManager == null) { 116 throw new IOException("No secret manager configured for token authentication"); 117 } 118 User currentUser = RpcServer.getRequestUser() 119 .orElseThrow(() -> new AccessDeniedException("No authenticated user for request!")); 120 UserGroupInformation ugi = currentUser.getUGI(); 121 if (!isAllowedDelegationTokenOp(ugi)) { 122 LOG.warn("Token generation denied for user=" + currentUser.getName() + ", authMethod=" 123 + ugi.getAuthenticationMethod()); 124 throw new AccessDeniedException( 125 "Token generation only allowed for Kerberos authenticated clients"); 126 } 127 128 Token<AuthenticationTokenIdentifier> token = 129 secretManager.generateToken(currentUser.getName()); 130 response.setToken(ClientTokenUtil.toToken(token)); 131 } catch (IOException ioe) { 132 CoprocessorRpcUtils.setControllerException(controller, ioe); 133 } 134 done.run(response.build()); 135 } 136 137 @Override 138 public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request, 139 RpcCallback<AuthenticationProtos.WhoAmIResponse> done) { 140 AuthenticationProtos.WhoAmIResponse.Builder response = 141 AuthenticationProtos.WhoAmIResponse.newBuilder(); 142 RpcServer.getRequestUser().ifPresent(requestUser -> { 143 response.setUsername(requestUser.getShortName()); 144 AuthenticationMethod method = requestUser.getUGI().getAuthenticationMethod(); 145 if (method != null) { 146 response.setAuthMethod(method.name()); 147 } 148 }); 149 done.run(response.build()); 150 } 151}