HDFSRPC安全认证Token篇

本文主要阐述HDFSRPC安全认证相关的实现。主要介绍Token相关的实现。

写在前面

相关blog

https://blog.csdn.net/hncscwc/article/details/124722784

https://blog.csdn.net/hncscwc/article/details/124958357

Token由来

在探究完Kerberos,我一直在想一个问题,rpcConnection已经完成了验证,那为何还需要token?首先需要对yarn有一定的了解,我们知道mapreduce框架是把目标变成多个map,然后reduce出结果。Yarn在执行多个map、reduce的时候,是通过container来运行的。Container本质上是一个独立程序,执行了yarn分配的任务。当Container进程要去访问hdfs的时候,如果使用Kerberos,kdc验证服务存在的不可靠和性能问题(多机多container并发极高)必然会极大的限制大数据平台的稳定,尤其是当有大量用户请求需要通过kdc来获取tgt票据时。因此Token认证被引入充当kerberos的补充,在兼顾安全认证的同时,性能没有较大的损耗。在hadoop中,token主要包括DelegationToken,以及其他一些token,例如之前文件介绍过的BlockToken,以及yarn中一系列的token。

Token中yarn container流程图

1863_1.png

Token的应用

当完成kerberos验证以后,服务主体的可以通过getDelegationToken接口来获取token。当服务主体下面的的进程需要去访问hdfs的时候,可以通过token来访问。

Token的验证也在rpc的sasl中,但是步骤跟简单,如下:

1863_2.png

server当收到client negotiate请求以后,会返回多个auth。

auths {method: "TOKEN"mechanism: "DIGEST-MD5"protocol: ""serverId: "default"challenge: "realm=\"default\",nonce=\"svFDnzmhsk40oN5z6vnUFgYgawR17w+XvxiX1Z3M\",charset=utf-8,algorithm=md5-sess"
}
auths {method: "KERBEROS"mechanism: "GSSAPI"protocol: "root"serverId: "node17"
}

client接收完negotiate应答后,可以通过服务主体获取的token来initSaslClient,然后发送Initiate请求。Server接收到Initiate请求,会通过token初始化saslServer,不同于Kerberos,saslserver验证完token会立马complete。这时候server会直接返回success应答给客户端。客户端接收到success应答以后即完成SaslClient的初始化。

可以看出token验证的整个过程更简单,而且本质上就是server验证了一下client的token,消耗更少,性能更高。

token验证本身与用户密码生成没有任何关系,主要都是java原生类来实现。代码如下:

public class TokenTest {public static final String SASL_DEFAULT_REALM = "default";public static final String USERNAME = "tokentestuser";public static final char[] PASSWORD = new char[]{'1'};public static void main(String[] args) throws SaslException {String mechanism = "DIGEST-MD5";CallbackHandler serverCallback = new SaslDigestCallbackHandler();String protocol = "";String serverId = SASL_DEFAULT_REALM;SaslServer saslServer = FastSaslServerFactory.getInstance().createSaslServer(mechanism, protocol, serverId, null, serverCallback);String saslUser = null;Map<String, String> saslProperties = new HashMap<String, String>();saslProperties.put("javax.security.sasl.qop", "auth");saslProperties.put("javax.security.sasl.server.authentication", "true");CallbackHandler clientCallback = new SaslClientCallbackHandler();SaslClient saslClient = Sasl.createSaslClient(new String[]{mechanism}, saslUser, protocol, serverId, saslProperties, clientCallback);byte[] response = saslServer.evaluateResponse(new byte[0]);System.out.println("NEGOTIATE:" + new String(response));byte[] request = saslClient.evaluateChallenge(response);System.out.println("INITIATE:" + new String(request));byte[] response2 = saslServer.evaluateResponse(request);System.out.println("SUCCESS:" + new String(response2));System.out.println("server complete:" + saslServer.isComplete());saslClient.evaluateChallenge(response2);System.out.println("client complete:" + saslClient.isComplete());}public static class SaslDigestCallbackHandler implements CallbackHandler {@Overridepublic void handle(Callback[] callbacks) throws UnsupportedCallbackException {NameCallback nc = null;PasswordCallback pc = null;AuthorizeCallback ac = null;for (Callback callback : callbacks) {if (callback instanceof AuthorizeCallback) {ac = (AuthorizeCallback) callback;} else if (callback instanceof NameCallback) {nc = (NameCallback) callback;} else if (callback instanceof PasswordCallback) {pc = (PasswordCallback) callback;} else if (callback instanceof RealmCallback) {continue; // realm is ignored} else {throw new UnsupportedCallbackException(callback,"Unrecognized SASL DIGEST-MD5 Callback");}}if (pc != null) {pc.setPassword(PASSWORD);}if (ac != null) {String authid = ac.getAuthenticationID();String authzid = ac.getAuthorizationID();if (authid.equals(authzid)) {ac.setAuthorized(true);} else {ac.setAuthorized(false);}if (ac.isAuthorized()) {ac.setAuthorizedID(authzid);}}}}private static class SaslClientCallbackHandler implements CallbackHandler {private final String userName;private final char[] userPassword;public SaslClientCallbackHandler() {this.userName = USERNAME;this.userPassword = PASSWORD;}@Overridepublic void handle(Callback[] callbacks)throws UnsupportedCallbackException {NameCallback nc = null;PasswordCallback pc = null;RealmCallback rc = null;for (Callback callback : callbacks) {if (callback instanceof RealmChoiceCallback) {continue;} else if (callback instanceof NameCallback) {nc = (NameCallback) callback;} else if (callback instanceof PasswordCallback) {pc = (PasswordCallback) callback;} else if (callback instanceof RealmCallback) {rc = (RealmCallback) callback;} else {throw new UnsupportedCallbackException(callback,"Unrecognized SASL client callback");}}if (nc != null) {nc.setName(userName);}if (pc != null) {pc.setPassword(userPassword);}if (rc != null) {rc.setText(rc.getDefaultText());}}}
}

程序运行输出:

**NEGOTIATE:**realm=“default”,nonce=“alYJcFcQ1r8azJmG4E+9Vy4HJt7AfNyJIXhGCvcD”,charset=utf-8,algorithm=md5-sess

INITIATE:charset=utf-8,username=“tokentestuser”,realm=“default”,nonce=“alYJcFcQ1r8azJmG4E+9Vy4HJt7AfNyJIXhGCvcD”,nc=00000001,cnonce=“nA2o8sejSYExOtEt8ELnWJXob3KDHOIF2OlaxozQ”,digest-uri=“/default”,maxbuf=65536,response=e388c2b4a0f68f94607e01b033ef61b2,qop=auth

SUCCESS:rspauth=af3865533148b4f6539b785ce2958854

server complete:true

client complete:true

Client当收到server的negotiate response后,会通过某个算法生成response,然后发送initiate request。Server会通过同样的算法来生成自己的验证值来比较response,成功以后会同样的算法生成rspauth。Client收到rspauth以后,会用同样的算法来成自己的验证值来比较rspauth。

Token验证算法

protected byte[] generateResponseValue(String authMethod,String digestUriValue,String qopValue,String usernameValue,String realmValue,char[] passwdValue,byte[] nonceValue,byte[] cNonceValue,int nonceCount,byte[] authzidValue) throws NoSuchAlgorithmException,UnsupportedEncodingException,IOException {MessageDigest md5 = MessageDigest.getInstance("MD5");byte[] hexA1, hexA2;ByteArrayOutputStream A2, beginA1, A1, KD;// A2// --// A2 = { "AUTHENTICATE:", digest-uri-value,// [:00000000000000000000000000000000] }  // if auth-int or auth-conf//A2 = new ByteArrayOutputStream();A2.write((authMethod + ":" + digestUriValue).getBytes(encoding));if (qopValue.equals("auth-conf") ||qopValue.equals("auth-int")) {logger.log(Level.FINE, "DIGEST04:QOP: {0}", qopValue);A2.write(SECURITY_LAYER_MARKER.getBytes(encoding));}if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST05:A2: {0}", A2.toString());}md5.update(A2.toByteArray());byte[] digest = md5.digest();hexA2 = binaryToHex(digest);if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST06:HEX(H(A2)): {0}", new String(hexA2));}// A1// --// H(user-name : realm-value : passwd)//beginA1 = new ByteArrayOutputStream();beginA1.write(stringToByte_8859_1(usernameValue));beginA1.write(':');// if no realm, realm will be an empty stringbeginA1.write(stringToByte_8859_1(realmValue));beginA1.write(':');beginA1.write(stringToByte_8859_1(new String(passwdValue)));md5.update(beginA1.toByteArray());digest = md5.digest();if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST07:H({0}) = {1}",new Object[]{beginA1.toString(), new String(binaryToHex(digest))});}// A1// --// A1 = { H ( {user-name : realm-value : passwd } ),// : nonce-value, : cnonce-value : authzid-value//A1 = new ByteArrayOutputStream();A1.write(digest);A1.write(':');A1.write(nonceValue);A1.write(':');A1.write(cNonceValue);if (authzidValue != null) {A1.write(':');A1.write(authzidValue);}md5.update(A1.toByteArray());digest = md5.digest();H_A1 = digest; // Record H(A1). Use for integrity & privacy.hexA1 = binaryToHex(digest);if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST08:H(A1) = {0}", new String(hexA1));}//// H(k, : , s);//KD = new ByteArrayOutputStream();KD.write(hexA1);KD.write(':');KD.write(nonceValue);KD.write(':');KD.write(nonceCountToHex(nonceCount).getBytes(encoding));KD.write(':');KD.write(cNonceValue);KD.write(':');KD.write(qopValue.getBytes(encoding));KD.write(':');KD.write(hexA2);if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST09:KD: {0}", KD.toString());}md5.update(KD.toByteArray());digest = md5.digest();byte[] answer = binaryToHex(digest);if (logger.isLoggable(Level.FINE)) {logger.log(Level.FINE, "DIGEST10:response-value: {0}",new String(answer));}return (answer);}

本质上是把用户名密码和一些参数,放入MD5.update之中,最终生成一个MD5值。

值得注意的是生成response和rspauth时只有第一个参数authMethod不一样,一个为

AUTHENTICATE,一个为空字符串。

 digestResp.write(generateResponseValue("AUTHENTICATE",digestUri, negotiatedQop, username,negotiatedRealm, passwd, nonce, cnonce,nonceCount, authzidBytes));byte[] expected = generateResponseValue("",digestUri, negotiatedQop, username, negotiatedRealm,passwd, nonce, cnonce,  nonceCount, authzidBytes);               

Token的统一管理

Hadoop中Delegation Tokens的生成和验证主要依赖于HMAC机制。但是实际的实现可以自定义。主要原因是由于生成和验证都是在server端实现。Token相关的rpc接口如下:

Token<DelegationTokenIdentifier> getDelegationToken(Text renewer) throws IOException;
long renewDelegationToken(Token<DelegationTokenIdentifier> token) throws IOException;
void cancelDelegationToken(Token<DelegationTokenIdentifier> token) throws IOException;

一般的密码生成实现是生成用户名密码,存入数据库,然后通过查表验证。Token的实现略有不同,由于是HMAC来生成密码,所以密码是实时生成的,但是要保存HMAC的key,类似于AES256算法的key,key也不是固定的,是会变化的,所以要记录key。所以Token的持久化主要是持久化key和Token,也是通过proto格式来存,在fsimage.proto中。

message SecretManagerSection {message DelegationKey {optional uint32 id         = 1;optional uint64 expiryDate = 2;optional bytes  key        = 3;}message PersistToken {optional uint32 version        = 1;optional string owner          = 2;optional string renewer        = 3;optional string realUser       = 4;optional uint64 issueDate      = 5;optional uint64 maxDate        = 6;optional uint32 sequenceNumber = 7;optional uint32 masterKeyId    = 8;optional uint64 expiryDate     = 9;}optional uint32 currentId = 1;optional uint32 tokenSequenceNumber = 2;optional uint32 numKeys = 3;optional uint32 numTokens = 4;// repeated DelegationKey keys// repeated PersistToken tokens
}

独立站原文

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/762736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python基本概念和基本数据类型

一、基本概念 1.变量 变量是编程语言中最基本的概念&#xff0c;和字面意思一样&#xff0c;指的就是他们的值可变&#xff0c;和我们以前学习的方程类似&#xff0c;变量可以代入任何值。 命名规范&#xff1a;变量一般使用&#xff1a; 英文字母、下划线 和 数字组成 2.关键…

3.21 day2 QT

自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 要求: 1.需要使用Ui界面文件进行界面设计 2.ui界面上的组件相关设置&#xff0c;通过代码实现 3需要添加适当的动图

模拟堆(详解+例题)

一、定义 维护一个数据集合&#xff0c;堆是一个完全二叉树。 那么什么是二叉树呢&#xff1f; 如图&#xff1a; 二、关于小根堆实现 性质&#xff1a;每个根节点都小于等于左右两边&#xff0c;所以树根为最小值。 2.1、堆存储&#xff08;用一维数组来存&#xff09; 记住…

GifGun2汉化版点击渲染失败,弹窗提示“lossless不是有效的模板名称”

总算解决了&#xff0c;记录一下方法&#xff1a; 1&#xff09;在AE顶部导航&#xff0c;点击“编辑 > 模板 > 输出模块” 2&#xff09;新建一个名为GifGun的输出模块&#xff0c;为后续GifGun引用做准备。&#xff08;取名随意&#xff09; 3&#xff09;在AE顶部导航…

软件测试教程 自动化测试之Junit框架

文章目录 1. 什么是 Junit &#xff1f;2. 常见的注解2.1 Test2.2 BeforeAll&#xff0c;AfterAll2.3 BeforeEach&#xff0c;AfterEach 3. 测试用例顺序指定4. 参数化4.1 单个参数4.2 多个参数4.3 通过方法生成 5. 测试套件6. 断言6.1 断言相等6.2 断言不相等6.3 断言为空6.4 …

山东省大数据局副局长禹金涛一行莅临聚合数据走访调研

3月19日&#xff0c;山东省大数据局党组成员、副局长禹金涛莅临聚合数据展开考察调研。山东省大数据局数据应用管理与安全处处长杨峰&#xff0c;副处长都海明参加调研&#xff0c;苏州市大数据局副局长汤晶陪同。聚合数据董事长左磊等人接待来访。 调研组一行参观了聚合数据展…

软件设计师笔记

计算机 运算器组成&#xff1a;算术逻辑单元(ALU)、累加寄存器(AC)、数据缓冲寄存器(DR)、状态条件寄存器()等组成。 控制器组成&#xff1a;指令寄存器(IR)、程序计数器(PC)、地址寄存器(AR)、指令译码器(ID)。 最小数据单位&#xff1a;bit 最小存储单位: byte n进制 转 1…

蓝桥杯单片机备战——关于573问题的填坑

一、遇到的问题 还记得我前面在封装继电器外设的时候遇到的这个问题嘛&#xff0c;当时我怀疑的是138译码器在切换通道的时候会出现其他暂态导致已经锁定的573解锁。 其实不然&#xff0c;之所以会这样还是因为代码问题&#xff0c;也可以说是573反应时间太快了。下面我就分析…

麒麟系统中使用nginx发布项目

1. 安装Nginx sudo apt-get update #进行所有安装操作前都要执行这一句 sudo apt install nginx #出现询问就Yes参考具体 Nginx—在linux的ubuntu系统上的安装使用 2. 修改发布文件 将打包好的dist文件夹中的所有文件覆盖下面这个文件夹中的所有文件 如果出现没有权限替…

openEuler 22.03(华为欧拉)一键安装 Oracle 19C RAC(19.22) 数据库

前言 Oracle 一键安装脚本&#xff0c;演示 openEuler 22.03 一键安装 Oracle 19C RAC 过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&#xff1a;Shell脚本安装Oracle数据库…

OPPO案例 | Alluxio在DataAI湖仓一体的实践

分享嘉宾&#xff1a; 付庆午-OPPO数据架构组大数据架构师 在OPPO的实际应用中&#xff0c;我们将自研的Shuttle与Alluxio完美结合&#xff0c;使得整个Shuttle Service的性能得到显著提升&#xff0c;基本上实现了性能翻倍的效果。通过这一优化&#xff0c;我们成功降低了约一…

BetterDisplay Pro:让屏幕管理更高效、更便捷

BetterDisplay Pro是一款功能强大的显示器管理软件&#xff0c;适用于Windows和Mac操作系统。其主要功能包括显示器校准、自动校准、多种预设模式、手动校准以及可视化数据等。 具体而言&#xff0c;这款软件可以根据用户的需求和环境条件调整显示器的颜色、亮度和对比度等参数…

53、简述GCN、NIR、FMIR技术在脑机BCI的发展调查[什么?你咋也叫王富贵?]

最近在搞GCN处理EEG&#xff0c;调查了十几篇文献&#xff0c;总结了一些东西&#xff0c;和学生分享一下&#xff0c;此处只分享一些较为浅显的知识。如下&#xff1a; GCN在其他领域的应用&#xff1a; 1、计算机视觉&#xff1a; 图卷积神经网络在计算机视觉中的应用包括图…

好用的客服快捷回复软件推荐

在当今快节奏的商业环境中&#xff0c;客户服务的效率和质量已经成为企业成功的关键因素之一。对于客服工作人员来说&#xff0c;面对海量的客户咨询和问题解答&#xff0c;如何快速而准确地回复&#xff0c;成为了他们日常工作中的一大挑战。选择一款好用的快捷回复工具是非常…

刷题DAY30 | LeetCode 332-重新安排行程 51-N皇后 37-解数独

332 重新安排行程&#xff08;hard&#xff09; 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&…

SSC9211_USB-CAM解决方案

一、方案描述 SSC9211是一种用于USB-CAM应用程序跟场景的高度集成的SOC产品。平台本身基于ARM层-A7双核&#xff0c;内置16位&#xff0c;64M的DDR2&#xff0c;集成了图像传感器接口、高级ISP、高性能JPEG编码器和其他丰富的外设接口。支持单&#xff0c;双 MIPI sensor方案&…

目标检测——植物病害数据集

植物病害是植物正常状态的偏离&#xff0c;会破坏或改变其生命功能。植物病害会导致严重的产量损失&#xff0c;全球潜在损失估计高达16%。因此&#xff0c;研究植物病害以及开发诊断和治疗它们的方法是植物病理学领域的重要研究内容。 有效识别植物病害对于采取有效的控制措施…

Go语言学习Day1:什么是Go?

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、走近Go①Go语言的Logo②Go语言的创始人③Go语…

八大排序算法之希尔排序

希尔排序是插入排序的进阶版本&#xff0c;他多次调用插入排序&#xff0c;在插入排序上进行了改造&#xff0c;使其处理无序的数据时候更快 核心思想&#xff1a;1.分组 2.直接插入排序&#xff1a;越有序越快 算法思想&#xff1a; 间隔式分组&#xff0c;利用直接插入排序…

IoT物联网可以带来什么?——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的飞速发展&#xff0c;IoT物联网已逐渐渗透到我们生活的方方面面&#xff0c;它以其独特的方式&#xff0c;将各种设备、系统和人连接起来&#xff0c;为我们带来了前所未有的便利和惊喜。那么&#xff0c;IoT物联网究竟可以为我…