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; 023 024import java.io.IOException; 025import java.util.Collections; 026 027import org.apache.hadoop.hbase.CoprocessorEnvironment; 028import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor; 029import org.apache.hadoop.hbase.coprocessor.HasRegionServerServices; 030import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 031import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 032import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; 033import org.apache.hadoop.hbase.ipc.RpcServer; 034import org.apache.hadoop.hbase.ipc.RpcServerInterface; 035import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos; 036import org.apache.hadoop.hbase.regionserver.RegionServerServices; 037import org.apache.hadoop.hbase.security.AccessDeniedException; 038import org.apache.hadoop.hbase.security.User; 039import org.apache.hadoop.security.UserGroupInformation; 040import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 041import org.apache.hadoop.security.token.SecretManager; 042import org.apache.hadoop.security.token.Token; 043import org.apache.yetus.audience.InterfaceAudience; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Provides a service for obtaining authentication tokens via the 049 * {@link AuthenticationProtos} AuthenticationService coprocessor service. 050 */ 051@CoreCoprocessor 052@InterfaceAudience.Private 053public class TokenProvider implements AuthenticationProtos.AuthenticationService.Interface, 054 RegionCoprocessor { 055 056 private static final Logger LOG = LoggerFactory.getLogger(TokenProvider.class); 057 058 private AuthenticationTokenSecretManager secretManager; 059 060 061 @Override 062 public void start(CoprocessorEnvironment env) { 063 // if running at region 064 if (env instanceof RegionCoprocessorEnvironment) { 065 RegionCoprocessorEnvironment regionEnv = (RegionCoprocessorEnvironment)env; 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 (authMethod != AuthenticationMethod.KERBEROS 093 && authMethod != AuthenticationMethod.KERBEROS_SSL 094 && authMethod != AuthenticationMethod.CERTIFICATE) { 095 return false; 096 } 097 return true; 098 } 099 100 // AuthenticationService implementation 101 102 @Override 103 public Iterable<Service> getServices() { 104 return Collections.singleton( 105 AuthenticationProtos.AuthenticationService.newReflectiveService(this)); 106 } 107 108 @Override 109 public void getAuthenticationToken(RpcController controller, 110 AuthenticationProtos.GetAuthenticationTokenRequest request, 111 RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) { 112 AuthenticationProtos.GetAuthenticationTokenResponse.Builder response = 113 AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder(); 114 115 try { 116 if (secretManager == null) { 117 throw new IOException( 118 "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}