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}