简介
Kerberos 是一种网络认证协议,主要用于特定的场景下,代替传统的token方式,以一种更繁琐,但更安全的方式来认证用户信息。它通过票据 (ticket) 机制,确保用户在网络中与服务之间进行加密通信,并且避免了密码在网络中传输。Kerberos 的设计目标是通过可信的第三方(即 Key Distribution Center, KDC)管理用户身份,提供单点登录 (SSO) 和安全的认证方式。
Kerberos认证机制
身份验证
相较于传统的输入账号密码返回token的方式,Kerberos提供了一个更加安全的机制:
首先,Kerberos的核心功能依赖于KDC服务器完成。需要通过验证客户端用户信息来保证数据安全的的服务,不在接收账号密码来验证客户端身份。而是由管理员手动将KDC密钥,复制到目标服务及客户端,目标服务和客户端在通过密钥文件向KDC服务器发送请求,请求票据。KDC服务器在完成用户身份验证时,会返回一个功能类似于token的信息,他被称作票据。客户端也可以通过账号密码来获取TGT,不过部分服务驱动不支持。
Kerberos第一次需要携带管理员配置的密钥来向KDC服务器请求授予票据(TGT)。在客户端获得了TGT后,会在次向票据授予服务器(TGS)发送请求,由TGS来验证TGT内部包含的用户信息,来返回当前用户权限内的目标服务器的验证票据。
TGS服务器是KDC服务器的一部分。
票据验证
Kerberos还会验证当前客户端的其他信息,来保证票据在传输过程中的安全性,包括:
-
票据授予票据 (TGT) 的验证:TGS在处理客户端请求时,会验证TGT的完整性,以及根据TGT内记录的用户信息,来分配对应的权限票据。
-
服务票据的验证:服务端在接收到客户端的请求时,会检查票据的正确性和过期时间,来完成对客户端的用户验证。
-
身份的双向验证(可选):Kerberos 支持双向身份验证,其实用对成加密的方式,客户端和服务器都会互相验证对方身份,防止其他中间者试图破解票据。
-
服务端根据票据信息进一步验证:票据中存储着Kerberos中保存的用户信息,目标服务器通过解密,可以根据获取到票据中的信息,进一步对用户权限进行控制
-
加密强度和协议验证:KDC 和客户端在认证过程中会协商使用的加密算法。Kerberos 支持多种加密算法(如 AES、DES 等),并会验证使用的加密强度是否符合安全要求。通常,系统管理员可以配置 KDC 只接受某些强度的加密算法。
-
重放攻击的防护:Kerberos 通过多个机制防止重放攻击,包括Kerberos客户端在请求头中添加时间戳密文,服务验证请求头的时间戳和当前服务器的时间差,是否在允许范围内。一个票据同时只能在一个回话使用,不能在两个会话中同时使用。票据中包含客户端的 IP 地址等网络信息,防止票据在其他设备上被仿造。
重放攻击是指攻击者利用已经发送的数据,在此向目标服务器发送,伪装自己为已经验证完成的客户端端身份。通过对时间戳加密,并且判断时间差,可以有效的防止这一攻击。
kerberos架构
kerberos内部通过领域来分割多个系统,当一个主体通过密钥获取TGT后,他可以通过这个TGT获取这个领域中,当前主体权限范围内的所有其他主体的票据(权限控制在acl_files配置的文件中修改,后面会说),不过他无法获取其他的领域的票据,因为多个领域之间的TGT是不互通的。
不过我们可以对多个领域之间进行单向信任或双向信任,比如说一个服务系统,想要利用另一个服务系统的功能,我们只需要把两个领域进行互相信任即可,这样两者的TGT就可以互通,在一个领域内通过密钥获取主体的TGT,可以在两个领域中随意获取所有主体的票据。
在一个领域中通过主体来区分多个服务,比如说我想讲Kafka集群和redis集群来增加认证机制,那么我就要在kerberos中来添加对应的两个主体。例如kafka/admin@EXAMPLE.com,redis/admin@EXAMPLE.com,这就是两个主体,其中第一个名字是角色,他决定了这个主体拥有了什么权限,而第二个名字是当前角色的实例,用于区分多个相同权限和领域的主体。
通过领域和主体,我们可以实现更加精细的客户端权限认证控制。整体架构如下:
Kerberos基本使用
1. 安装 Kerberos 服务
yum install krb5-server krb5-libs krb5-workstation
2. 配置 Kerberos 配置文件
修改 /etc/krb5.conf
文件来配置 Kerberos 服务。内容如下:
[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 = FILE:/etc/pki/tls/certs/ca-bundle.crt
#默认领域:未指定领域的主体会默认指向这个领域
default_realm = EXAMPLE.COM
#Kerberos 默认的缓存票据存储位置
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
#配置领域的KDC服务器(kdc)地址,和KDC管理服务(admin_server)的地址
EXAMPLE.COM = {
#这里我们为了方便,直接使用了ip,管理其他服务时,一定要写成域名
kdc = 192.168.166.21
admin_server = 192.168.166.21
}
#可以配置多个领域
EXAMPLE2.COM = {
kdc = 192.168.166.21
admin_server = 192.168.166.21
}
[domain_realm]
#以.example.com结尾会匹配到这个领域
.example.com = EXAMPLE.COM
#精确匹配example.com
example.com = EXAMPLE.COM
3. 配置 KDC
KDC 的配置文件通常位于 /var/kerberos/krb5kdc/kdc.conf
。配置内容如下:
#KDC服务器全句默认配置
[kdcdefaults]
#kdc监听端口端口,默认值就是 88
kdc_ports = 88
#kdctcp连接端口
kdc_tcp_ports = 88
#配置领域局部配置
[realms]
EXAMPLE.COM = {
#kdc监听端口端口
kdc_port = 88
#票据的最大有效时间
max_life = 24h
#票据的最大可续展时间
max_renewable_life = 7d
#主密钥(Master Key)的存储文件
key_stash_file = /var/kerberos/krb5kdc/.k5.EXAMPLE.COM
#存储主体信息的数据库文件路径
database_name = /var/kerberos/krb5kdc/principal
#主密钥的加密类型。这里使用了 aes256-cts,即 256 位 AES 加密算法
master_key_type = aes256-cts
#管理员访问控制列表 (ACL) 文件的路径
acl_file = /var/kerberos/krb5kdc/kadm5.acl
#密码字典文件的路径
dict_file = /usr/share/dict/words
#keytab 文件路径
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
}
#可以配置多个领域
EXAMPLE2.COM = {
kdc_port = 88
max_life = 24h
max_renewable_life = 24
key_stash_file = /var/kerberos/krb5kdc/.k5.EXAMPLE2.COM
database_name = /var/kerberos/krb5kdc/2
master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/2.acl
dict_file = /usr/share/dict/2
admin_keytab = /var/kerberos/krb5kdc/2.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
}
在如上配置中,出现了很多难以理解的概念,我在下面进行统一解释:
在 [kdcdefaults] 中的配置是默认配置,当 [realms] 内地领域配置没有明确指定的配置,就会采用默认配置替代。
票据续展允许用户在票据的初始有效期到期之前,通过续展来延长票据的有效期,而不必重新输入用户名和密码。和token刷新过期时间类似,不过最长时间不能超过max_renewable_life配置的最长可续展时间,我们的配置中配置的是7天,也就是7天一过便无法在进行续展。
主密钥是KDC服务器对自身数据库进行加密解密的密钥。
管理员访问控制列表文件指定了哪些用户或组可以进行 KDC 管理操作,如添加、删除 Kerberos 主体等。
密码字典使用来生成一切初始密码,以及检查密码强度使用的。
keytab文件包含了 KDC 管理服务的密钥。该文件用于远程管理 Kerberos 实例,通过 kadmin
工具进行管理操作。
4. 初始化 Kerberos 数据库
通过如下命令初始化数据库
kdb5_util create -s -r <领域名>
可以省略-r,用来初始化默认领域,结果如下:
通过-r指定领域,结果如下:
进入目标目录,可以看到数据库文件初始化成功,内容如下:
5. 设置管理员权限
上文中我们提到acl_file配置是指定管理员访问控制列表文件的文件路径,我们可以通过管理员访问控制列表文件来设置KDC管理服务用户的权限,不过在管理权限之前,我们必须设置一个拥有全部权限的管理员,这个文件的默认内容如下:
其中前面是匹配主体的匹配字符串,后面是权限,可以看到一切以admin为实例名的主体都可以获取*也就是全部的权限。我们也可以自定义规则。
6. 启动 Kerberos 服务
完成以上的配置我们就可以启动服务了,由于kerberos是为其他服务来添加认证服务的,其他服务依赖于kerberos服务本身,所以我们最好将其设置为开机自启,命令如下:
sudo systemctl start krb5kdc
sudo systemctl start kadmin
sudo systemctl enable krb5kdc
sudo systemctl enable kadmin
其中krb5kdc时KDC服务器,而kadmin则是管理服务
7. 添加管理员用户
我们之前配置了管理员访问控制列表文件的配置,并且启动了管理服务,那么现在就可以通过管理服务来添加管理员账号,命令如下:
#进入交互终端
kadmin.local
#添加管理员主体
addprinc admin/admin
结果如下:
8. 测试服务功能
首先通过kinit来获取票据
kinit admin/admin
通过klist查看票据
klist
成功后结果如下:
Kerberos集成Kafka服务
kerberos虽然已经搭建成功,并成功获取票据,可是他该如何和其他服务结合使用呢,这里我们以Kafka举例
1. 搭建kafka集群
2. 修改hosts文件
kerberos的主体匹配极度依赖于域名,所以我们一定要将所有的服务地址存到域名中来管理,即使是在同一台主机。
/etc/hosts
:
192.168.166.51 www.kdc.com
192.168.166.51 www.kadmin.com
192.168.166.51 www.kafka.com
3. 修改Kerberos领域配置
krb5.conf
是 Kerberos 客户端和 KDC(Key Distribution Center)都需要的配置文件,它用于定义 Kerberos 领域、KDC 地址、管理服务器地址以及 Kerberos 客户端如何与 KDC 进行交互。所以在kerberos服务的主机,以及客户端,Kafka服务的主机,都需要读取这个配置文件。不过我们是在一个主机上进行测试的,所以不用额外创建。
/etc/krb5.conf
:
# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/
[logging]
default = FILE:/var/log/krb5libs.log
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 = FILE:/etc/pki/tls/certs/ca-bundle.crt
default_realm = KAFKA.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
KAFKA.COM = {
kdc = www.kdc.com
admin_server = www.kadmin.com
}
[domain_realm]
.kafka.com = KAFKA.COM
www.kafka.com = KAFKA.COM
kdc.conf
是 Kerberos KDC 服务器的配置文件,用于配置 KDC 服务的行为,例如票据的有效期、领域的策略、加密类型等。这个文件只需要在运行 KDC 服务 的机器上配置。
/var/kerberos/krb5kdc/kdc.conf
:
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
KAFKA.COM = {
#kdc监听端口端口
kdc_port = 88
#票据的最大有效时间
max_life = 24h
#票据的最大可续展时间
max_renewable_life = 7d
#主密钥(Master Key)的存储文件
key_stash_file = /var/kerberos/krb5kdc/.k5.EXAMPLE.COM
#存储主体信息的数据库文件路径
database_name = /var/kerberos/krb5kdc/principal
#主密钥的加密类型。这里使用了 aes256-cts,即 256 位 AES 加密算法
master_key_type = aes256-cts
#管理员访问控制列表 (ACL) 文件的路径
acl_file = /var/kerberos/krb5kdc/kadm5.acl
#密码字典文件的路径
4. 初始化Kerberos领域的数据库
kdb5_util create -s -r KAFKA.COM
结果如下:
更改配置后要重新初始化一个新的仓库,不知道为什么,但是我更改配置后重启,断开服务器连接,各种方法都尝试了,都没有更改成功,重新初始化数据库后才成功。
5. 在kerberos中创建kafka服务的主体
#进入终端
kadmin.local
#创建主体,生成随机密钥
addprinc -randkey kafka/www.kafka.com@KAFKA.COM
这个地方中间的实例一定要用域名,客户端在获取TGT后,再次获取票据时不会发送完整的主体名,而是只发送第一部分的服务名,第二部分是根据请求的域名来填充的。
成功结果如下
6. 导出密钥文件
#进入终端
kadmin.local
#导出密钥生成密钥文件
ktadd -k /var/kerberos/krb5kdc/kafka.keytab kafka/www.kafka.com@KAFKA.COM
成功结果如下:
7. 启动Kerberos服务
sudo systemctl start krb5kdc
sudo systemctl start kadmin
#设置开机自启
sudo systemctl enable krb5kdc
sudo systemctl enable kadmin
8. 修改Kafka的配置文件
在 listeners
配置中启用 SASL_PLAINTEXT 或 SASL_SSL:
listeners=SASL_PLAINTEXT://:9092
inter.broker.listener.name=SASL_PLAINTEXT
配置 SASL 的 Kerberos 认证机制:
sasl.mechanism.inter.broker.protocol=GSSAPI
sasl.enabled.mechanisms=GSSAPI
为 Kerberos 认证指定相关的命令和参数:
kerberos.kinit.cmd=/usr/bin/kinit
kerberos.min.time.before.relogin=60000
9. 创建 Kafka Broker 的 JAAS 配置文件
KRaft 模式下,您依然需要为 Kafka Broker 配置 JAAS 文件,并将之前导出的密钥文件保存在keyTab配置项指定的目录。创建或修改 Kafka Broker 的 JAAS 文件,例如 kafka_server_jaas.conf
:
KafkaServer {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
keyTab="/var/kerberos/krb5kdc/kafka.keytab"
storeKey=true
useTicketCache=false
principal="kafka/www.kafka.com@KAFKA.COM"
#注意只有最后一行的结尾有分号
serviceName="kafka";
};
10. 将JAAS配置文件路径导入当前命令行的环境变量
java命令的启动参数,用于Kafka启动时能够寻找到kafka_server_jaas.conf
文件的位置,我们也可以将这个参数直接放在Kafka的启动脚本第一行。
我的文件路径是/etc/kafka/kafka_server_jaas.conf
,所以我的命令如下
export KAFKA_OPTS="-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf"
11. 获取TGT
通过kinit,指定密钥文件路径和主体名来获取TGT,命令如下
kinit -k -t /var/kerberos/krb5kdc/kafka.keytab kafka/www.kafka.com@REALM
通过klist可以查看我们的票据是否获取成功,结果如下:
可以看到还有过期时间,以及可续展时间。
12. 启动Kafka
正常启动,没什么好说的。
./bin/kafka-server-start.sh ./config/kraft/server.properties
13. 测试
1. 直接创建主体
当我们直接创建主体
./bin/kafka-topics.sh --create --topic test-topic --bootstrap-server 192.168.166.51:9092 --replication-factor 1 --partitions 1
得到结果如下:
可以看我们无法创建主体,可以证明此时Kafka已经没办法正常访问,现在我们需要将创建主体的命令也是用和Kafka服务一样的协议配置,并且携带票据,才能够访问。
2. 创建配置文件
创建配置文件 /kafka/config/kraft/client.properties
内容如下:
security.protocol=SASL_PLAINTEXT
sasl.mechanism=GSSAPI
sasl.kerberos.service.name=kafka
sasl.kerberos.service.name配置是配置服务名,客户端在向KDC请求票据时,KDC服务器会将客户端传递的服务名,拼接上客户端请求的域名,以及领域,组成一个主体,这样可以不仅可以区分客户端请求不同的服务,还能根据客户端请求域名的不同,查询不同的主体,分配给不同客户端不同的权限。
实际生产中,服务不应该和客户端使用同一个主体,应该每一种权限的客户端一个主体,服务自己一个主体,然后通过acl分配给客户端主体不同服务的权限,来达到让客户端可以访问服务的目的,还能对客户端进行权限控制。不过我们这里主要是测试功能,所以服务和客户端使用一个主体,方便一些。
3. 创建 客户端 的 JAAS 配置文件
和服务一样,客户端想要获取票据,叶需要创建一个JAAS配置文件/etc/kafka/kafka_client_jaas.conf
,文件内容也和服务端的差不多,内容如下:
KafkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
renewTicket=true
useKeyTab=true
keyTab="/var/kerberos/krb5kdc/kafka.keytab"
principal="kafka/www.kafka.com@KAFKA.COM";
};
在测试过程中,客户端和服务器在一个主机中,并且又是同一个主体所以可以和服务器使用同一个密钥文件。配置相同路径(keyTab)即可。
4. 添加环境变量
和服务一样,不多说了
export KAFKA_OPTS="-Djava.security.auth.login.config=/etc/kafka/kafka_client_jaas.conf"
5. 获取TGT
和服务端一样,命令如下:
kinit -k -t /var/kerberos/krb5kdc/kafka.keytab kafka/www.kafka.com@REALM
6. 重新创建主体
重现创建主体,添加我们之间创建的配置文件,命令如下:
./bin/kafka-topics.sh --create --topic test-topic --bootstrap-server www.kafka.com:9092 --replication-factor 1 --partitions 1 --command-config ./config/kraft/client.properties
注意:这里一定要用域名,原因上面已经说了,会拼接这个域名,组成主体名
结果如下:
7. 验证主体是否创建成功
同样的,查询主体的命令也可以通过添加配置成功运行。命令如下:
./bin/kafka-topics.sh --list --bootstrap-server www.kafka.com:9092 --command-config ./config/kraft/client.properties
结果如下: