本文为HDFSRPC安全认证Token的补充。主要阐述Token的结构,Token相关RPC接口以及Token如何使用。
写在前面
请先阅读HDFSRPC安全认证Token篇。
Token简介
Token作为Kerberos的补充,当完成kerberos验证以后,服务主体的可以通过getDelegationToken接口来获取token,后续client端都可以通过Token来访问文件系统。那Token被盗用,非法客户端是否可以访问文件系统?答案是肯定的。因为Token本身就是访问凭证,里面包含了id,password等用于登录验证。
Token的结构
tokenproto的结构其实比较简单。
message TokenProto {
required bytes identifier = 1;
required bytes password = 2;
required string kind = 3;
required string service = 4;
}
identifier:tokenid。
password:此token的密码。
kind:token的kind,HDFS为HDFS_DELEGATION_TOKEN。
service:为fs.defaultFs中的host:port
其中比较复杂的identifier,可以发现identifier为bytes,而不是常见的int,UUID等,因为identifier其实多个数据拼接而成的。值得注意的hadoop本身支持自有存储自定义自己TokenIdentifier的,包括如何自己生成identifier的bytes。但是也可以直接实现hadoop的AbstractDelegationTokenIdentifier类,而hdfs就是实现了AbstractDelegationTokenIdentifier类。
AbstractDelegationTokenIdentifier.java
public abstract class AbstractDelegationTokenIdentifier extends TokenIdentifier {
private static final byte VERSION = 0;
private Text owner;
private Text renewer;
private Text realUser;
private long issueDate;
private long maxDate;
private int sequenceNumber;
private int masterKeyId = 0;
...
@Override
public void readFields(DataInput in) throws IOException {
byte version = in.readByte();
if (version != VERSION) {
throw new IOException("Unknown version of delegation token " +
version);
}
owner.readFields(in, Text.DEFAULT_MAX_LEN);
renewer.readFields(in, Text.DEFAULT_MAX_LEN);
realUser.readFields(in, Text.DEFAULT_MAX_LEN);
issueDate = WritableUtils.readVLong(in);
maxDate = WritableUtils.readVLong(in);
sequenceNumber = WritableUtils.readVInt(in);
masterKeyId = WritableUtils.readVInt(in);
}
@VisibleForTesting
void writeImpl(DataOutput out) throws IOException {
out.writeByte(VERSION);
owner.write(out);
renewer.write(out);
realUser.write(out);
WritableUtils.writeVLong(out, issueDate);
WritableUtils.writeVLong(out, maxDate);
WritableUtils.writeVInt(out, sequenceNumber);
WritableUtils.writeVInt(out, masterKeyId);
}
...
}
owner:token的所有者,其实就是kerberos的用户。
renewer:token的更新者。
realuser:涉及hadoop的proxyuser,暂无介绍。
issueDate,maxData:创建时间,过期时间。
sequenceNumber:token序列号。
masterKeyId:默认为0,暂无介绍。
值得注意的是read和write方法,里面包含identifier bytes的结构。bytes本质上是由version和各个属性顺序写入组成。
Token的sasl验证
由于id和password都为bytes,sasl会使用base64加密以后再作为sasl的user,password使用。
SaslRpcClient.java
private static class SaslClientCallbackHandler implements CallbackHandler {
private final String userName;
private final char[] userPassword;
public SaslClientCallbackHandler(Token<? extends TokenIdentifier> token) {
this.userName = SaslRpcServer.encodeIdentifier(token.getIdentifier());
this.userPassword = SaslRpcServer.encodePassword(token.getPassword());
}
...
}
SaslRpcServer.java
static String encodeIdentifier(byte[] identifier) {
return new String(Base64.encodeBase64(identifier), StandardCharsets.UTF_8);
}
static char[] encodePassword(byte[] password) {
return new String(Base64.encodeBase64(password), StandardCharsets.UTF_8).toCharArray();
}
验证本质上客户端会发送id和由password生成sign到server,server会根据token id获取password,以相同的算法生成sign,然后比较,如果相同就验证成功,反之则失败。
Token的使用
hdfs fetchdt是专门用于token相关的操作。
生成token文件
使用hdfs fetchdt test.token可以生成token文件,本质上调用rpc getDelegationToken接口。生成token之前要保证Kerberos已经登录(kinit user)。
查看token文件
使用hdfs fetchdt --print --verbose test.token可以查看token文件。
使用token访问文件系统
官网上并没有使用token访问文件系统的教程。根据UserGroupInformation.java源码可以通过设置环境变量HADOOP_TOKEN_FILE_LOCATION来使用token文件访问文件系统。
UserGroupInformation.java
String fileLocation = System.getenv(HADOOP_TOKEN_FILE_LOCATION);
if (fileLocation != null) {
// Load the token storage file and put all of the tokens into the
// user. Don't use the FileSystem API for reading since it has a lock
// cycle (HADOOP-9212).
File source = new File(fileLocation);
LOG.debug("Reading credentials from location set in {}: {}",
HADOOP_TOKEN_FILE_LOCATION,
source.getCanonicalPath());
if (!source.isFile()) {
throw new FileNotFoundException("Source file "
+ source.getCanonicalPath() + " from "
+ HADOOP_TOKEN_FILE_LOCATION
+ " not found");
}
Credentials cred = Credentials.readTokenStorageFile(
source, conf);
LOG.debug("Loaded {} tokens", cred.numberOfTokens());
loginUser.addCredentials(cred);
}
使用export HADOOP_TOKEN_FILE_LOCATION=/token_path/test.token来设置。后续使用hdfs dfs命令都会使用token,即使没有kinit,也能访问文件系统。如果token文件无法使用了,可以通过unset HADOOP_TOKEN_FILE_LOCATION来重新使用kerberos访问文件系统,获取新tokenfile等。
token的renew,cancel
hdfs fetchdt --renew test.token命令对应rpc renewDelegationToken接口。
hdfs fetchdt --cancel test.token命令对应rpc cancelDelegationToken接口。
喜欢就点赞、收藏一下~