1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hbase.security.token; 20 21 import java.io.IOException; 22 23 import com.google.protobuf.RpcCallback; 24 import com.google.protobuf.RpcController; 25 import com.google.protobuf.Service; 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.apache.hadoop.hbase.classification.InterfaceAudience; 29 import org.apache.hadoop.hbase.Coprocessor; 30 import org.apache.hadoop.hbase.CoprocessorEnvironment; 31 import org.apache.hadoop.hbase.coprocessor.CoprocessorService; 32 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 33 import org.apache.hadoop.hbase.ipc.RpcServer; 34 import org.apache.hadoop.hbase.ipc.RpcServerInterface; 35 import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 36 import org.apache.hadoop.hbase.protobuf.ResponseConverter; 37 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos; 38 import org.apache.hadoop.hbase.security.AccessDeniedException; 39 import org.apache.hadoop.hbase.security.User; 40 import org.apache.hadoop.security.UserGroupInformation; 41 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; 42 import org.apache.hadoop.security.token.SecretManager; 43 import org.apache.hadoop.security.token.Token; 44 45 /** 46 * Provides a service for obtaining authentication tokens via the 47 * {@link AuthenticationProtos} AuthenticationService coprocessor service. 48 */ 49 @InterfaceAudience.Private 50 public class TokenProvider implements AuthenticationProtos.AuthenticationService.Interface, 51 Coprocessor, CoprocessorService { 52 53 private static final Log LOG = LogFactory.getLog(TokenProvider.class); 54 55 private AuthenticationTokenSecretManager secretManager; 56 57 58 @Override start(CoprocessorEnvironment env)59 public void start(CoprocessorEnvironment env) { 60 // if running at region 61 if (env instanceof RegionCoprocessorEnvironment) { 62 RegionCoprocessorEnvironment regionEnv = 63 (RegionCoprocessorEnvironment)env; 64 RpcServerInterface server = regionEnv.getRegionServerServices().getRpcServer(); 65 SecretManager<?> mgr = ((RpcServer)server).getSecretManager(); 66 if (mgr instanceof AuthenticationTokenSecretManager) { 67 secretManager = (AuthenticationTokenSecretManager)mgr; 68 } 69 } 70 } 71 72 @Override stop(CoprocessorEnvironment env)73 public void stop(CoprocessorEnvironment env) throws IOException { 74 } 75 76 /** 77 * @param ugi A user group information. 78 * @return true if delegation token operation is allowed 79 */ isAllowedDelegationTokenOp(UserGroupInformation ugi)80 private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException { 81 AuthenticationMethod authMethod = ugi.getAuthenticationMethod(); 82 if (authMethod == AuthenticationMethod.PROXY) { 83 authMethod = ugi.getRealUser().getAuthenticationMethod(); 84 } 85 if (authMethod != AuthenticationMethod.KERBEROS 86 && authMethod != AuthenticationMethod.KERBEROS_SSL 87 && authMethod != AuthenticationMethod.CERTIFICATE) { 88 return false; 89 } 90 return true; 91 } 92 93 // AuthenticationService implementation 94 95 @Override getService()96 public Service getService() { 97 return AuthenticationProtos.AuthenticationService.newReflectiveService(this); 98 } 99 100 @Override getAuthenticationToken(RpcController controller, AuthenticationProtos.GetAuthenticationTokenRequest request, RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done)101 public void getAuthenticationToken(RpcController controller, 102 AuthenticationProtos.GetAuthenticationTokenRequest request, 103 RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) { 104 AuthenticationProtos.GetAuthenticationTokenResponse.Builder response = 105 AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder(); 106 107 try { 108 if (secretManager == null) { 109 throw new IOException( 110 "No secret manager configured for token authentication"); 111 } 112 113 User currentUser = RpcServer.getRequestUser(); 114 UserGroupInformation ugi = null; 115 if (currentUser != null) { 116 ugi = currentUser.getUGI(); 117 } 118 if (currentUser == null) { 119 throw new AccessDeniedException("No authenticated user for request!"); 120 } else if (!isAllowedDelegationTokenOp(ugi)) { 121 LOG.warn("Token generation denied for user="+currentUser.getName() 122 +", authMethod="+ugi.getAuthenticationMethod()); 123 throw new AccessDeniedException( 124 "Token generation only allowed for Kerberos authenticated clients"); 125 } 126 127 Token<AuthenticationTokenIdentifier> token = 128 secretManager.generateToken(currentUser.getName()); 129 response.setToken(ProtobufUtil.toToken(token)).build(); 130 } catch (IOException ioe) { 131 ResponseConverter.setControllerException(controller, ioe); 132 } 133 done.run(response.build()); 134 } 135 136 @Override whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request, RpcCallback<AuthenticationProtos.WhoAmIResponse> done)137 public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request, 138 RpcCallback<AuthenticationProtos.WhoAmIResponse> done) { 139 User requestUser = RpcServer.getRequestUser(); 140 AuthenticationProtos.WhoAmIResponse.Builder response = 141 AuthenticationProtos.WhoAmIResponse.newBuilder(); 142 if (requestUser != null) { 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 } 152