Kerberos
Kerberos是一种计算机网络认证协议,它允许某实体在非安全网络环境下通信,向另一个实体以一种安全的方式证明自己的身份,协议基于对称密码学,并需要一个值得信赖的第三方(KDC)。
它主要包含:认证服务器(AS)和票据授权服务器(TGS)组成的密钥分发中心(KDC),以及提供特定服务的SS。相关概念描述:
- AS(Authentication Server)= 认证服务器
- KDC(Key Distribution Center)= 密钥分发中心
- TGT(Ticket Granting Ticket)= 授权票据
- TGS(Ticket Granting Server)= 票据授权服务器
- SS(Service Server)= 特定服务提供端
KDC持有一个密钥数据库;每个网络实体——无论是客户还是服务器——共享一套只有他自己和KDC知道的密钥。密钥的内容用于证明实体的身份。对于两个实体间的通信,KDC产生一个会话密钥,用来加密他们之间的交互信息。
Kerberos认证流程
- 客户端认证
- 客户端将clientID(Principal)以明文消息发送给AS,以代表用户请求服务,密码(keytab)不会发送,AS能够从本地数据库中查询到。
- AS会查询该用户名是否存在本地数据库,如果存在则使用该用户密码,发送两条加密消息给客户端。
- Message A: 用户密码加密的会话密钥Client/TGS Session Key 。
- Message B: 使用TGS加密的TGT,TGT包含了( client ID, client network address, ticket validity period, and the client/TGS session key)。
- 一旦客户端收到消息A和B,它就会使用密码生成的密钥解密消息A。如果用户输入的密码与AS数据库中的密码不匹配,则无法解密消息A。解密后的会话密钥用于与TGS进行进一步的通信,客户端无法解密消息B,因为它是使用TGS的密钥加密的。此时,客户端具有足够的信息来向TGS进行身份验证。
- 服务授权
- 请求服务时,客户端将以下消息发送到TGS。
- Message C: 由消息B的TGT和请求的服务的ID组成。
- Message D: 使用Client/TGS Session Key加密的authenticator (which is composed of the client ID and the timestamp)。
- 收到消息C和消息D后,TGS首先检查KDC数据库中是否存在所需的服务,查找到之后,TGS用自己的密钥解密消息C中的消息B(也就是TGT),从而得到之前生成的Client/TGS Session Key。TGS再用这个Session Key解密消息D得到包含用户ID和时间戳的Authenticator,并对TGT和Authenticator进行验证,验证通过之后返回两条消息:
- Message E: 由服务端SS加密的Client-to-server ticket(客户端到服务端票据),包含了(Client ID, client network address, validity period and Client/Server Session Key)。
- Message F:由Client/TGS Session Key加密的Client/Server Session Key
- 服务请求
- 当获得Client/Server Session Key之后,Client就能够使用服务器提供的服务了。Client向指定服务器发出2条消息:
- Message E: 服务端ss加密的Client-to-server ticket
- Message G: 新的Authenticator,包含使用Client/Server Session Key加密的client ID, timestamp。
- 服务端解密消息E进而获得Client/Server Session Key,再用这个Client/Server Session Key解密消息G得到Authenticator,同TGS一样对Ticket和Authenticator进行验证,验证通过则服务器将以下消息发送给客户端,以确认其真实身份和接收客户端请求。
- Message H: 通过Client/Server Session Key加密的,在客户的身份验证器中找到的时间戳。
- 客户端使用Client/Server Session Key解密确认(消息H),并检查时间戳是否正确。如果正确,则客户端可以信任服务器并可以开始向服务器发出服务请求。
- 服务器向客户端提供请求的服务。
kdc集群:
安装包
- krb5-server:Kerberos服务端程序,KDC所在节点。
- krb5-workstation: 包含一些基本Kerberos程序,比如(kinit, klist, kdestroy,kpasswd),使用Kerberos的所有节点都应该部署。
- krb5-devel: 包含编译Kerberos程序的头文件和一些类库,所有节点部署。
相关文件:
- /etc/krb5.conf : 客户端根据该文件中的信息取访问KDC
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
EXAMPLE.COM = {
kdc = k8s01:88
admin_server = k8s01
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
- [logging]:表示 server 端的日志的打印位置
- [libdefaults]:每种连接的默认配置,需要注意以下几个关键的小配置
- default_realm = EXAMPLE.COM:设置 Kerberos 应用程序的默认领域。如果您有多个领域,只需向 [realms] 节添加其他的语句。
- ticket_lifetime: 表明凭证生效的时限,一般为24小时。
- renew_lifetime: 表明凭证最长可以被延期的时限,一般为一个礼拜。当凭证过期之后,对安全认证的服务的后续访问则会失败。
- clockskew:时钟偏差是不完全符合主机系统时钟的票据时戳的容差,超过此容差将不接受此票据。通常,将时钟扭斜设置为 300 秒(5 分钟)。这意味着从服务器的角度看,票证的时间戳与它的偏差可以是在前后 5 分钟内。
- udp_preference_limit= 1:禁止使用 udp 可以防止一个 Hadoop 中的错误
- [realms]:列举使用的 realm。
- kdc:代表要 kdc 的位置。格式是 机器:端口
- admin_server:代表 admin 的位置。格式是 机器:端口
- default_domain:代表默认的域名
- [appdefaults]:可以设定一些针对特定应用的配置,覆盖默认配置。
- /var/kerberos/krb5kdc/kdc.conf:kdc的专属配置,可以根据自己的需求修改下kdc数据库的存放目录。
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
EXAMPLE.COM = {
#master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
}
- /var/kerberos/krb5kdc/kadm5.acl: 权限相关配置
其中前一个*号是通配符,表示像名为“abc/admin”或“xxx/admin”的人都可以使用此工具(远程或本地)
管理kerberos数据库,后一个*跟权限有关,*表示所有权限
*/admin@EXAMPLE.COM *
术语
- principal :认证的主体,简单来说就是"用户名"。 格式: 服务/主机@域
- realm:域,类似于namespace的作用,可以看成是principal的一个"容器"或者"空间"。 在kerberos, 大家都约定成俗用大写来命名realm, 比如"EXAMPLE.COM"
- keytab文件:存储了多个principal的加密密码。
相关指令
创建完KDC数据库以及管理员后,从server端操作KDC.
- kinit : 登录,测试账户有效性
- kinit root/admin@EXAMPLE.COM 基于密码
- kinit -kt /root/maqi1.keytab maqi@EXAMPLE.COM 基于keytab
- klist
- klist: 查询登录状态
- klist -ket /root/maqi1.keytab : 查看keytab记录的主体
- kdestroy: 退出登录
- kadmin.local:登录KDC后台,只有安装krb5-server节点才能登录
- listprincs: 查看用户列表
- change_password admin/admin@EXAMPLE.COM :修改密码
- add_principal maqi : 创建用户,输入密码
- add_principal -randkey maqi1: 随机生成密码
- get_principal :获取凭证信息
- delete_principal maqi: 删除用户
- xst -k admin.keytab -norandkey admin/admin@EXAMPLE.COM :导出用户keytab文件(并且不要修改密码)
- xst -k maqi1.keytab maqi maqi1 : 导出用户keytab文件,将多个认证主体写入一个keytab文件
Hbase认证参数
同步模式
必填参数:
- hbase.security.authentication= kerberos
- hbase.security.authorization = true
- hbase.master.kerberos.principal = hbase/_HOST@TDH #hbase服务端principal
- hbase.regionserver.kerberos.principal = hbase/_HOST@TDH
- 客户端认证参数:clientPrincipal = 'dfs@TDH''
- 客户端认证参数:clientKeytabFile = 'hdfs.keytab'
选填参数:
- 环境变量:java.security.krb5.conf = krb5.conf
- zk是否开启sasl认证:zookeeper.sasl.client = true
FlinkStreamsql使用注意事项:
- keytab文件通过FlinkShipfile模式已经上传,通过("user.dir")进行全路径拼接
- 同步方式,zookeeper.znode.parent参数没有hase前缀,而异步则需要hbase前缀需要特别注意。
认证部分代码:
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", host);
// 注意没有hbase前缀,否则使用默认/hbase路径
conf.set("zookeeper.znode.parent", zkParent);
fillSyncKerberosConfig(conf, regionserverPrincipal, zookeeperSaslClient, securityKrb5Conf);
clientKeytabFile = System.getProperty("user.dir") + File.separator + clientKeytabFile;
clientPrincipal = !StringUtils.isEmpty(clientPrincipal) ? clientPrincipal : regionserverPrincipal;
// 使用客户端clientKeytabFile,clientPrincipal
UserGroupInformation userGroupInformation = HbaseConfigUtils.loginAndReturnUGI(conf, clientPrincipal, clientKeytabFile);
org.apache.hadoop.conf.Configuration finalConf = conf;
conn = userGroupInformation.doAs(new PrivilegedAction<Connection>() {
@Override
public Connection run() {
try {
// 服务端授权时,取出hbase/host@TDH中的 服务ID和host构建SASLClient
// RpcClientImpl#Connection
// HBaseSaslRpcClient
return ConnectionFactory.createConnection(finalConf);
} catch (IOException e) {
LOG.error("Get connection fail with config:{}", finalConf);
throw new RuntimeException(e);
}
}
});
private void fillSyncKerberosConfig(org.apache.hadoop.conf.Configuration config, String regionserverPrincipal,
String zookeeperSaslClient, String securityKrb5Conf) throws IOException {
config.set("hbase.master.kerberos.principal", regionserverPrincipal);
config.set("hbase.regionserver.kerberos.principal", regionserverPrincipal);
config.set("hbase.security.authorization", "true");
config.set("hbase.security.authentication", "kerberos");
if (!StringUtils.isEmpty(zookeeperSaslClient)) {
System.setProperty("zookeeper.sasl.client", zookeeperSaslClient);
}
if (!StringUtils.isEmpty(securityKrb5Conf)) {
// 默认已经上传到container
String krb5ConfPath = System.getProperty("user.dir") + File.separator + securityKrb5Conf;
System.setProperty("java.security.krb5.conf", krb5ConfPath);
}
}
异步模式
必填参数
- hbase.sasl.clientconfig=Client
- hbase.security.auth.enable=true
- hbase.security.authentication=kerberos
- hbase.kerberos.regionserver.principa=hbase/_HOST@TDH #hbase服务端principal
- hbase.sasl.clientconfig=/xxx/jaas.conf
jaas.conf文件格式样例:
Client {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
useTicketCache=false
keyTab="/path/to/keytab.keytab" ## 客户端认证参数
principal="myhbaseuser@MY.HADOOP.DOMAIN"; ## 客户端认证参数
};
选填参数:
- 环境变量:java.security.krb5.conf = krb5.conf
- zk是否开启sasl认证:zookeeper.sasl.client = true
flinkStreamsql使用注意事项:
- keytab文件通过FlinkShipfile模式已经上传,通过("user.dir")进行全路径拼接。
- 异步模式下Hbase在ZK的根路径参数hbase.zookeeper.znode.parent有hase前缀。
- jaas.conf文件根据参数进行构建,并保存在("user.dir")下。
认证部分代码:
Config config = new Config();
config.overrideConfig("hbase.zookeeper.quorum", hbaseSideTableInfo.getHost());
config.overrideConfig("hbase.zookeeper.znode.parent", hbaseSideTableInfo.getParent());
fillAsyncKerberosConfig(config, hbaseSideTableInfo);
hBaseClient = new HBaseClient(config, executorService);
private void fillAsyncKerberosConfig(Config config, HbaseSideTableInfo hbaseSideTableInfo) throws IOException {
AuthUtil.JAASConfig jaasConfig = HbaseConfigUtils.buildJaasConfig(hbaseSideTableInfo);
String jaasFilePath = AuthUtil.creatJaasFile("JAAS", ".conf", jaasConfig);
config.overrideConfig("java.security.auth.login.config", jaasFilePath);
config.overrideConfig("hbase.security.auth.enable", "true");
config.overrideConfig("hbase.sasl.clientconfig", "Client");
config.overrideConfig("hbase.security.authentication" "kerberos");
String regionserverPrincipal = hbaseSideTableInfo.getRegionserverPrincipal();
config.overrideConfig("hbase.kerberos.regionserver.principal", regionserverPrincipal);
if (!StringUtils.isEmpty(hbaseSideTableInfo.getZookeeperSaslClient())) {
System.setProperty("zookeeper.sasl.client", hbaseSideTableInfo.getZookeeperSaslClient());
}
if (!StringUtils.isEmpty(hbaseSideTableInfo.getSecurityKrb5Conf())) {
String krb5ConfPath = System.getProperty("user.dir") + File.separator + hbaseSideTableInfo.getSecurityKrb5Conf();
LOG.info("krb5ConfPath:{}", krb5ConfPath);
System.setProperty("java.security.krb5.conf", krb5ConfPath);
}
}
相关问题记录
- 从IDEA提交到Kerberos集群异常。需要从当前环境变量配置krb5.conf路径。
- /hbase节点找不到,同步模式下zookeeper.znode.parent默认路径为Hbase,确认参数没有hbase.前缀.
- 服务授权阶段hbase.master.kerberos.principal使用的principal和client的principal为一个,且格式不正确,必须为xxxx/_HOST@XXX格式。