| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hdfs.tools; |
| |
| import java.io.BufferedReader; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.HttpURLConnection; |
| import java.net.InetAddress; |
| import java.net.URI; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.PrivilegedExceptionAction; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.hdfs.DistributedFileSystem; |
| import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; |
| import org.apache.hadoop.hdfs.server.namenode.CancelDelegationTokenServlet; |
| import org.apache.hadoop.hdfs.server.namenode.GetDelegationTokenServlet; |
| import org.apache.hadoop.hdfs.server.namenode.RenewDelegationTokenServlet; |
| import org.apache.hadoop.io.IOUtils; |
| import org.apache.hadoop.io.Text; |
| import org.apache.hadoop.security.Credentials; |
| import org.apache.hadoop.security.SecurityUtil; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.apache.hadoop.security.token.Token; |
| import org.apache.hadoop.util.StringUtils; |
| |
| /** |
| * Fetch a DelegationToken from the current Namenode and store it in the |
| * specified file. |
| */ |
| public class DelegationTokenFetcher { |
| private static final String USAGE = |
| "fetchdt retrieves delegation tokens (optionally over http)\n" + |
| "and writes them to specified file.\n" + |
| "Usage: fetchdt [--webservice <namenode http addr>] <output filename>"; |
| |
| private final DistributedFileSystem dfs; |
| private final UserGroupInformation ugi; |
| private final DataOutputStream out; |
| private final Configuration conf; |
| private static final Log LOG = |
| LogFactory.getLog(DelegationTokenFetcher.class); |
| |
| static { |
| // Enable Kerberos sockets |
| System.setProperty("https.cipherSuites", "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"); |
| } |
| |
| /** |
| * Command-line interface |
| */ |
| public static void main(final String [] args) throws Exception { |
| // Login the current user |
| UserGroupInformation.getCurrentUser().doAs(new PrivilegedExceptionAction<Object>() { |
| @Override |
| public Object run() throws Exception { |
| |
| if(args.length == 3 && "--webservice".equals(args[0])) { |
| getDTfromRemoteIntoFile(args[1], args[2]); |
| return null; |
| } |
| // avoid annoying mistake |
| if(args.length == 1 && "--webservice".equals(args[0])) { |
| System.out.println(USAGE); |
| return null; |
| } |
| if(args.length != 1 || args[0].isEmpty()) { |
| System.out.println(USAGE); |
| return null; |
| } |
| |
| DataOutputStream out = null; |
| |
| try { |
| Configuration conf = new Configuration(); |
| DistributedFileSystem dfs = (DistributedFileSystem) FileSystem.get(conf); |
| out = new DataOutputStream(new FileOutputStream(args[0])); |
| UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); |
| |
| new DelegationTokenFetcher(dfs, out, ugi, conf).go(); |
| |
| out.flush(); |
| System.out.println("Succesfully wrote token of size " + |
| out.size() + " bytes to "+ args[0]); |
| } catch (IOException ioe) { |
| System.out.println("Exception encountered:\n" + |
| StringUtils.stringifyException(ioe)); |
| } finally { |
| if(out != null) out.close(); |
| } |
| return null; |
| } |
| }); |
| |
| } |
| |
| public DelegationTokenFetcher(DistributedFileSystem dfs, |
| DataOutputStream out, UserGroupInformation ugi, Configuration conf) { |
| checkNotNull("dfs", dfs); this.dfs = dfs; |
| checkNotNull("out", out); this.out = out; |
| checkNotNull("ugi", ugi); this.ugi = ugi; |
| checkNotNull("conf",conf); this.conf = conf; |
| } |
| |
| private void checkNotNull(String s, Object o) { |
| if(o == null) throw new IllegalArgumentException(s + " cannot be null."); |
| } |
| |
| public void go() throws IOException { |
| String fullName = ugi.getUserName(); |
| String shortName = ugi.getShortUserName(); |
| Token<DelegationTokenIdentifier> token = |
| dfs.getDelegationToken(new Text(fullName)); |
| |
| // Reconstruct the ip:port of the Namenode |
| URI uri = FileSystem.getDefaultUri(conf); |
| String nnAddress = |
| InetAddress.getByName(uri.getHost()).getHostAddress() + ":" + uri.getPort(); |
| token.setService(new Text(nnAddress)); |
| |
| Credentials ts = new Credentials(); |
| ts.addToken(new Text(shortName), token); |
| ts.writeTokenStorageToStream(out); |
| } |
| |
| /** |
| * Utility method to obtain a delegation token over http |
| * @param nnHttpAddr Namenode http addr, such as http://namenode:50070 |
| */ |
| static public Credentials getDTfromRemote(String nnAddr, |
| String renewer) throws IOException { |
| DataInputStream dis = null; |
| |
| try { |
| StringBuffer url = new StringBuffer(); |
| if (renewer != null) { |
| url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC).append("?"). |
| append(GetDelegationTokenServlet.RENEWER).append("=").append(renewer); |
| } else { |
| url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC); |
| } |
| System.out.println("Retrieving token from: " + url); |
| URL remoteURL = new URL(url.toString()); |
| SecurityUtil.fetchServiceTicket(remoteURL); |
| URLConnection connection = remoteURL.openConnection(); |
| |
| InputStream in = connection.getInputStream(); |
| Credentials ts = new Credentials(); |
| dis = new DataInputStream(in); |
| ts.readFields(dis); |
| return ts; |
| } catch (Exception e) { |
| throw new IOException("Unable to obtain remote token", e); |
| } finally { |
| if(dis != null) dis.close(); |
| } |
| } |
| |
| /** |
| * Renew a Delegation Token. |
| * @param nnAddr the NameNode's address |
| * @param tok the token to renew |
| * @return the Date that the token will expire next. |
| * @throws IOException |
| */ |
| static public long renewDelegationToken(String nnAddr, |
| Token<DelegationTokenIdentifier> tok |
| ) throws IOException { |
| StringBuilder buf = new StringBuilder(); |
| buf.append(nnAddr); |
| buf.append(RenewDelegationTokenServlet.PATH_SPEC); |
| buf.append("?"); |
| buf.append(RenewDelegationTokenServlet.TOKEN); |
| buf.append("="); |
| buf.append(tok.encodeToUrlString()); |
| BufferedReader in = null; |
| HttpURLConnection connection = null; |
| try { |
| URL url = new URL(buf.toString()); |
| SecurityUtil.fetchServiceTicket(url); |
| connection = (HttpURLConnection)url.openConnection(); |
| in = new BufferedReader(new InputStreamReader |
| (connection.getInputStream())); |
| long result = Long.parseLong(in.readLine()); |
| in.close(); |
| return result; |
| } catch (IOException ie) { |
| LOG.info("error in renew over HTTP", ie); |
| IOException e = null; |
| if(connection != null) { |
| String resp = connection.getResponseMessage(); |
| e = getExceptionFromResponse(resp); |
| } |
| |
| IOUtils.cleanup(LOG, in); |
| if(e!=null) { |
| LOG.info("rethrowing exception from HTTP request: " + e.getLocalizedMessage()); |
| throw e; |
| } |
| throw ie; |
| } |
| } |
| |
| static private IOException getExceptionFromResponse(String resp) { |
| String exceptionClass = "", exceptionMsg = ""; |
| if(resp != null && !resp.isEmpty()) { |
| String[] rs = resp.split(";"); |
| exceptionClass = rs[0]; |
| exceptionMsg = rs[1]; |
| } |
| LOG.info("Error response from HTTP request=" + resp + |
| ";ec=" + exceptionClass + ";em="+exceptionMsg); |
| IOException e = null; |
| if(exceptionClass != null && !exceptionClass.isEmpty()) { |
| if(exceptionClass.contains("InvalidToken")) { |
| e = new org.apache.hadoop.security.token.SecretManager.InvalidToken(exceptionMsg); |
| e.setStackTrace(new StackTraceElement[0]); // stack is not relevant |
| } else if(exceptionClass.contains("AccessControlException")) { |
| e = new org.apache.hadoop.security.AccessControlException(exceptionMsg); |
| e.setStackTrace(new StackTraceElement[0]); // stack is not relevant |
| } |
| } |
| LOG.info("Exception from HTTP response=" + e.getLocalizedMessage()); |
| return e; |
| } |
| |
| /** |
| * Cancel a Delegation Token. |
| * @param nnAddr the NameNode's address |
| * @param tok the token to cancel |
| * @throws IOException |
| */ |
| static public void cancelDelegationToken(String nnAddr, |
| Token<DelegationTokenIdentifier> tok |
| ) throws IOException { |
| StringBuilder buf = new StringBuilder(); |
| buf.append(nnAddr); |
| buf.append(CancelDelegationTokenServlet.PATH_SPEC); |
| buf.append("?"); |
| buf.append(CancelDelegationTokenServlet.TOKEN); |
| buf.append("="); |
| buf.append(tok.encodeToUrlString()); |
| BufferedReader in = null; |
| try { |
| URL url = new URL(buf.toString()); |
| SecurityUtil.fetchServiceTicket(url); |
| HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |
| if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { |
| throw new IOException("Error cancelling token:" + |
| connection.getResponseMessage()); |
| } |
| } catch (IOException ie) { |
| IOUtils.cleanup(LOG, in); |
| throw ie; |
| } |
| } |
| |
| /** |
| * Utility method to obtain a delegation token over http |
| * @param nnHttpAddr Namenode http addr, such as http://namenode:50070 |
| * @param filename Name of file to store token in |
| */ |
| static private void getDTfromRemoteIntoFile(String nnAddr, String filename) |
| throws IOException { |
| Credentials ts = getDTfromRemote(nnAddr, null); |
| |
| DataOutputStream file = new DataOutputStream(new FileOutputStream(filename)); |
| ts.writeTokenStorageToStream(file); |
| file.flush(); |
| System.out.println("Successfully wrote token of " + file.size() |
| + " bytes to " + filename); |
| } |
| } |