blob: a38250adf72459214cac6e372a656696dca20efc [file] [log] [blame]
/**
* 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);
}
}