In most of the enterprise Hadoop Clusters installation, the cluster is Kerberos enable.
Hence users need to either kinit when using shell based hadoop commands or when using JAVA required to use the Hadoop API
UserGroupInformation.loginUserFromKeytab(principal, keytab);
Which is fine, when you have a pregenerated Keytab file. But in most of enterprise eco systems, the principal user which runs the job in production environment has to retrieve the password from a Password Vault like ManageEngine, Centrify, Password Vault Manager etc. And hence the keytab has to be generated on the fly before calling
UserGroupInformation.loginUserFromKeytab(principal, keytab);
This can be done by generating the keytab using JAVA as following. If you are using Hadoop classpath jars, these imports will be included by default as hadoop internally uses the same ApacheDS jars. You may include the maven dependency if you using maven.
org.apache.directory.server
apacheds-kerberos-codec
2.0.0-M21
You can also use this createKeytab method to generate keytab with JAVA.
public static void createKeytab(File keytabFile, String principal, String passwd, String realm)
throws Exception {
Keytab keytab = new Keytab();
List entries = new ArrayList();
principal = principal + "@" + realm;
KerberosTime timestamp = new KerberosTime();
for (Map.Entry entry : KerberosKeyFactory
.getKerberosKeys(principal, passwd).entrySet()) {
EncryptionKey ekey = entry.getValue();
byte keyVersion = (byte) ekey.getKeyVersion();
entries
.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
}
keytab.setEntries(entries);
keytab.write(keytabFile);
}
If you go through the class below, I have created the keytab file as a temp file with RW for user only.
Look at the method
public static File createTempKeytabInDir(String dir)
The reason being, you do not want the file to be there when you exit the JAVA application as it contain the user credentials and let JVM handles this.
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.net.URI;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.components.EncryptionKey;
import java.util.Set;
import java.util.HashSet;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.Files;
public class TestKerberosWithCreateKeytab {
public static void main(String[] args) throws Exception {
System.out.println("krish");
File keytabFile = KrishUtils.createTempKeytabInDir("Some Dir or Home Dir");
//Get the password from Password Vault and pass here.
KrishUtils.createKeytab(keytabFile,"user","passwd","DOMAIN.EXAMPLE.COM");
String keytab = keytabFile.getName();
String principal = "user"+"@DOMAIN.EXAMPLE.COM";
//Do kerberos login here
KrishUtils.authenticate(keytab,principal);
//Do some HDFS operation to validate login
MyBackgroudMethod thread = new MyBackgroudMethod();
thread.start();
}
static class MyBackgroudMethod extends Thread {
@Override
public void run(){
while (true) {
System.out.println("Executed!");
try {
HdfsFileInfo hdfs = new HdfsFileInfo();
hdfs.callHdfs("hdfs://nameservice1","/tmp/krish.txt","checksum");
Thread.sleep(30*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
static class HdfsFileInfo
{
public void callHdfs(String nameservice,String file,String checksum) throws Exception {
Configuration config = null;
FileSystem fileSys = null;
try {
config = new Configuration();
fileSys = FileSystem.get(new URI(nameservice), config);
if (checksum.equals("checksum"))
{
System.out.println(fileSys.getFileChecksum(new Path(file)).toString());
}
}
catch (Exception e) {
System.out.println(e);
}
finally {
if (fileSys !=null) {
fileSys.close();
}
}
}
}
static class KrishUtils {
public static File createTempKeytabInDir(String dir) throws Exception{
File keytabFile = File.createTempFile("krish.keytab-",".tmp", new File(dir));
keytabFile.deleteOnExit();
Set perms = new HashSet();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
Files.setPosixFilePermissions(keytabFile.toPath(), perms);
return keytabFile;
}
public static void createKeytab(File keytabFile, String principal, String passwd, String realm)
throws Exception {
Keytab keytab = new Keytab();
List entries = new ArrayList();
principal = principal + "@" + realm;
KerberosTime timestamp = new KerberosTime();
for (Map.Entry entry : KerberosKeyFactory
.getKerberosKeys(principal, passwd).entrySet()) {
EncryptionKey ekey = entry.getValue();
byte keyVersion = (byte) ekey.getKeyVersion();
entries
.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
}
keytab.setEntries(entries);
keytab.write(keytabFile);
}
public static synchronized UserGroupInformation authenticate(String keytab, String principal)
throws AuthenticationFailed {
File kfile = new File(keytab);
if (!(kfile.isFile() && kfile.canRead())) {
throw new IllegalArgumentException("The keyTab file: "
+ keytab + " is nonexistent or can't read. "
+ "Please specify a readable keytab file for Kerberos auth.");
}
try {
principal = SecurityUtil.getServerPrincipal(principal, "");
} catch (Exception e) {
throw new AuthenticationFailed("Host lookup error when resolving principal " + principal, e);
}
try {
UserGroupInformation.loginUserFromKeytab(principal, keytab);
return UserGroupInformation.getLoginUser();
} catch (IOException e) {
throw new AuthenticationFailed("Login failed for principal " + principal, e);
}
}
static class AuthenticationFailed extends Exception {
public AuthenticationFailed(String reason, Exception cause) {
super("Kerberos Authentication Failed. " + reason, cause);
}
}
}
Post your questions and suggestions below, I will try to comment/reply to those.