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 */ 018 019 package org.apache.hadoop.mapreduce.security; 020 021 import java.io.IOException; 022 import java.util.List; 023 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 import org.apache.hadoop.classification.InterfaceAudience; 027 import org.apache.hadoop.classification.InterfaceStability; 028 import org.apache.hadoop.conf.Configuration; 029 import org.apache.hadoop.fs.FileSystem; 030 import org.apache.hadoop.fs.Path; 031 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 032 import org.apache.hadoop.io.Text; 033 import org.apache.hadoop.mapred.JobConf; 034 import org.apache.hadoop.mapred.Master; 035 import org.apache.hadoop.mapreduce.MRJobConfig; 036 import org.apache.hadoop.mapreduce.security.token.JobTokenIdentifier; 037 import org.apache.hadoop.security.Credentials; 038 import org.apache.hadoop.security.UserGroupInformation; 039 import org.apache.hadoop.security.token.Token; 040 import org.apache.hadoop.security.token.TokenIdentifier; 041 042 043 /** 044 * This class provides user facing APIs for transferring secrets from 045 * the job client to the tasks. 046 * The secrets can be stored just before submission of jobs and read during 047 * the task execution. 048 */ 049 @InterfaceAudience.Public 050 @InterfaceStability.Evolving 051 public class TokenCache { 052 053 private static final Log LOG = LogFactory.getLog(TokenCache.class); 054 055 056 /** 057 * auxiliary method to get user's secret keys.. 058 * @param alias 059 * @return secret key from the storage 060 */ 061 public static byte[] getSecretKey(Credentials credentials, Text alias) { 062 if(credentials == null) 063 return null; 064 return credentials.getSecretKey(alias); 065 } 066 067 /** 068 * Convenience method to obtain delegation tokens from namenodes 069 * corresponding to the paths passed. 070 * @param credentials 071 * @param ps array of paths 072 * @param conf configuration 073 * @throws IOException 074 */ 075 public static void obtainTokensForNamenodes(Credentials credentials, 076 Path[] ps, Configuration conf) throws IOException { 077 if (!UserGroupInformation.isSecurityEnabled()) { 078 return; 079 } 080 obtainTokensForNamenodesInternal(credentials, ps, conf); 081 } 082 083 /** 084 * Remove jobtoken referrals which don't make sense in the context 085 * of the task execution. 086 * 087 * @param conf 088 */ 089 public static void cleanUpTokenReferral(Configuration conf) { 090 conf.unset(MRJobConfig.MAPREDUCE_JOB_CREDENTIALS_BINARY); 091 } 092 093 static void obtainTokensForNamenodesInternal(Credentials credentials, 094 Path[] ps, Configuration conf) throws IOException { 095 for(Path p: ps) { 096 FileSystem fs = FileSystem.get(p.toUri(), conf); 097 obtainTokensForNamenodesInternal(fs, credentials, conf); 098 } 099 } 100 101 /** 102 * get delegation token for a specific FS 103 * @param fs 104 * @param credentials 105 * @param p 106 * @param conf 107 * @throws IOException 108 */ 109 @SuppressWarnings("deprecation") 110 static void obtainTokensForNamenodesInternal(FileSystem fs, 111 Credentials credentials, Configuration conf) throws IOException { 112 String delegTokenRenewer = Master.getMasterPrincipal(conf); 113 if (delegTokenRenewer == null || delegTokenRenewer.length() == 0) { 114 throw new IOException( 115 "Can't get Master Kerberos principal for use as renewer"); 116 } 117 boolean readFile = true; 118 119 String fsName = fs.getCanonicalServiceName(); 120 if (TokenCache.getDelegationToken(credentials, fsName) == null) { 121 //TODO: Need to come up with a better place to put 122 //this block of code to do with reading the file 123 if (readFile) { 124 readFile = false; 125 String binaryTokenFilename = 126 conf.get(MRJobConfig.MAPREDUCE_JOB_CREDENTIALS_BINARY); 127 if (binaryTokenFilename != null) { 128 Credentials binary; 129 try { 130 binary = Credentials.readTokenStorageFile( 131 new Path("file:///" + binaryTokenFilename), conf); 132 } catch (IOException e) { 133 throw new RuntimeException(e); 134 } 135 credentials.addAll(binary); 136 } 137 if (TokenCache.getDelegationToken(credentials, fsName) != null) { 138 LOG.debug("DT for " + fsName + " is already present"); 139 return; 140 } 141 } 142 List<Token<?>> tokens = 143 fs.getDelegationTokens(delegTokenRenewer, credentials); 144 if (tokens != null) { 145 for (Token<?> token : tokens) { 146 credentials.addToken(token.getService(), token); 147 LOG.info("Got dt for " + fs.getUri() + ";uri="+ fsName + 148 ";t.service="+token.getService()); 149 } 150 } 151 //Call getDelegationToken as well for now - for FS implementations 152 // which may not have implmented getDelegationTokens (hftp) 153 if (tokens == null || tokens.size() == 0) { 154 Token<?> token = fs.getDelegationToken(delegTokenRenewer); 155 if (token != null) { 156 credentials.addToken(token.getService(), token); 157 LOG.info("Got dt for " + fs.getUri() + ";uri=" + fsName 158 + ";t.service=" + token.getService()); 159 } 160 } 161 } 162 } 163 164 /** 165 * file name used on HDFS for generated job token 166 */ 167 @InterfaceAudience.Private 168 public static final String JOB_TOKEN_HDFS_FILE = "jobToken"; 169 170 /** 171 * conf setting for job tokens cache file name 172 */ 173 @InterfaceAudience.Private 174 public static final String JOB_TOKENS_FILENAME = "mapreduce.job.jobTokenFile"; 175 private static final Text JOB_TOKEN = new Text("ShuffleAndJobToken"); 176 177 /** 178 * 179 * @param namenode 180 * @return delegation token 181 */ 182 @SuppressWarnings("unchecked") 183 @InterfaceAudience.Private 184 public static Token<DelegationTokenIdentifier> getDelegationToken( 185 Credentials credentials, String namenode) { 186 //No fs specific tokens issues by this fs. It may however issue tokens 187 // for other filesystems - which would be keyed by that filesystems name. 188 if (namenode == null) 189 return null; 190 return (Token<DelegationTokenIdentifier>) credentials.getToken(new Text( 191 namenode)); 192 } 193 194 /** 195 * load job token from a file 196 * @param conf 197 * @throws IOException 198 */ 199 @InterfaceAudience.Private 200 public static Credentials loadTokens(String jobTokenFile, JobConf conf) 201 throws IOException { 202 Path localJobTokenFile = new Path ("file:///" + jobTokenFile); 203 204 Credentials ts = Credentials.readTokenStorageFile(localJobTokenFile, conf); 205 206 if(LOG.isDebugEnabled()) { 207 LOG.debug("Task: Loaded jobTokenFile from: "+ 208 localJobTokenFile.toUri().getPath() 209 +"; num of sec keys = " + ts.numberOfSecretKeys() + 210 " Number of tokens " + ts.numberOfTokens()); 211 } 212 return ts; 213 } 214 /** 215 * store job token 216 * @param t 217 */ 218 @InterfaceAudience.Private 219 public static void setJobToken(Token<? extends TokenIdentifier> t, 220 Credentials credentials) { 221 credentials.addToken(JOB_TOKEN, t); 222 } 223 /** 224 * 225 * @return job token 226 */ 227 @SuppressWarnings("unchecked") 228 @InterfaceAudience.Private 229 public static Token<JobTokenIdentifier> getJobToken(Credentials credentials) { 230 return (Token<JobTokenIdentifier>) credentials.getToken(JOB_TOKEN); 231 } 232 }