星期三, 三月 21, 2007

AA Center (1), Kerberos

AA 即 Authentication 和 Authorization,认证和授权。在企业的架构体系中,主要的目标就是要能够统一帐户和认证信息,比如我希望 http svn, ssh login, samba 等都可以使用同一个帐户来登录,而不是为每一个服务创建不同的帐户,你当然可以使用同样的用户名的密码,但实际上这些帐户却并不同步,修改一个并不能同时修改另一个,而且各个服务的密码加密方法不同,有些服务的密码强度不够,甚至使用明文传送,那么根据最短板原理,你整个系统的安全性就大有问题了。

授权是另一个方面的问题,以决定一个帐户能做什么。对于集中认证机制来说,应该就是我可以使用哪些服务了。

我打算采用 Kerberos 来实现集中认证。关于 Kerberos 的基本原理,可以参考Kerberos 的原理。这篇文章比较有趣,但为了更清楚的说明,做一个图来说明。

Kerberos 是一个三方认证体系,所以有 KDC, Client 和 Server 三台主机吧

/---------->KDC
| /------KDC
(1) |
| (2)
| |
| V
Client------(3)------>Server
(1) Authentication REQUEST to KDC
(2) KDC_REPLAY
KDC_REPLAY = TICKET, OTHER
OTHER = {client, server, K_session}K_user
TICKET = {client, server, start_time, lifetime, K_session}K_Server
(3) Authentication REQUEST to Server
REQUEST = AUTHENTICATOR, TICKET
AUTHENTICATOR = {user, addr}K_session
也就是说,KDC 和 Server 之间并没有直接联系,而是分享了一个共同的秘密,即在 TICKET 中的 K_session。Client 得到 TICKET 的时候,他不能更改其中的 K_session,因为它没有 K_Server,但它可以拿到 OTHER 中的 K_session,并用这个生成验证器 AUTHENTICATOR,它把验证器和票一起提交给 Server。而 Server 拿到 TICKET 后,可以用 K_Server 解密,并取出 K_session,并比较 AUTHENTICATOR 和 TICKET 中的内容并确保一只,以及 TICKET 中的时间没有过期。如果在(2)时用户更改了 K_session,那么 Client 生成的 AUTHENTICATOR 将无法被解密,这也就意味着这个 Client 无法通过 Server 的验证了。

下面看看 Kerberos 的基本配置。主要的软件包有 krb5-libs, krb5-server, krb5-workstation 以及 pam_krb5。

编辑 /etc/krb5.conf,将其中[realms]和[domain_realm]部分的 EXAMPLE.COM 的内容全部替换成自己的域名,例如:
[realms]
SHOPEX.CN = {
kdc = kdc.shopex.cn:88
admin_server = kdc.shopex.cn:749
default_domain = shopex.cn
}

[domain_realm]
.shopex.cn = SHOPEX.CN
shopex.cn = SHOPEX.CN
[kdc]
profile = /var/kerberos/krb5kdc/kdc.conf
然后保证 admin_server 即 kdc.shopex.cn 可以被解析,可以使用 DNS 或 /etc/hosts。

在上面的[kdc]部分,指明了 KDC 使用的配置文件,修改[realms]为自己的域。

然后创建数据库:
sh# /usr/kerberos/sbin/krb5_util create -r SHOPEX.CN -s
-r 指定 realm,-s 指定创建 stash 文件。stash 文件是主密钥的一个本地加密副本,主密钥用于自动验证 KDC,作为系统的启动序列的一节。这个命令在 kdc.conf 的[kdcdefaults]指定的目录 /var/kerberos/krb5kdc 中创建 Kerberos 数据库文件 principal.db 和 principal.ok,管理数据库文件 principal.kadm5,lock 和 stash(隐藏)等。

然后要编辑 /var/kerberos/krb5kdc/kadmin.acl,ACL 即 Access Control List(访问控制列表),这个文件控制对 Kerberos 自身数据库的访问权限,访问数据库使用 kadmin 程序,这是一个交互式程序,以便进行增删用户等操作。默认内容是
*/admin@SHOPEX.CN       *
一行,但通常我们会使用其他用户,所以增加:
rocky/admin@SHOPEX.CN   *
这样 rocky/admin@SHOPEX.CN 就用后了所有的管理权限,可以在 kadmin 中运行其所有的命令。如果不增加 rocky/admin 的权限,那么运行 kadmin 就会出现如下问题:
sh# kadmin
Authenticating as principal rocky/admin@SHOPEX.CN with password.
Password for rocky/admin@SHOPEX.CN:
kadmin: addprinc -randkey host/doc.shopex.cn
WARNING: no policy specified for host/doc.shopex.cn@SHOPEX.CN; defaulting to no policy
add_principal: Operation requires ``add'' privilege while creating "host/doc.shopex.cn@SHOPEX.CN".
不论 rocky/admin 是否用后 admin 权限,你都必须先创建这个用户。不过在 Kerberos 中并不称为用户,而是称为 Client 的 principal,principal 既可以是对 Client 的,也可以是对 Server 的,这在后面谈到。

因为还没有其他 principal,而且现在还没有启动 Kerberos 的 KDC 服务,所以不可能运行 kadmin。那么要创建这个 principal,只能运行 kadmin.local,这个程序只能在 KDC 上运行。而 kadmin 可以在 Kerberos Client 上运行以连接到 KDC。

那么运行:
sh# /usr/kerberos/sbin/kadmin.local
kadmin.local: addprinc rocky/admin
...
kadmin.local: addprinc rocky
...
后面这一次是增加一个"普通用户",其完整的 principal 即为 rocky@SHOPEX.CN(或 rocky/@SHOPEX.CN,后面为空)。

接着可以启动 KDC 和与之相关的其他服务了:
sh# /etc/init.d/krb5kdc start
sh# /etc/init.d/kadmin start
sh# /etc/init.d/krb524 start
然后看看客户端能否正常地从 KDC 上取得票据:

sh# kinit rocky
Password for rocky@SHOPEX.CN:
sh# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: rocky@SHOPEX.CN

Valid starting Expires Service principal
03/21/07 13:27:54 03/22/07 13:27:54 krbtgt/SHOPEX.CN@SHOPEX.CN


Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached
这个程序是在 KDC 上运行的,是属于 krb5-workstation 包的,因为客户端和 KDC 使用同样的配置文件 /etc/krb5.conf,所以如果在 KDC 上运行不需要额外再配置。整个过程即可以认为是前面图中的(1)(2)部分。上面可以看到已经拿到了票据,cache 在 /tmp/krb5cc_0 中,因为文件权限设定,所以不可能为其他用户所用,保证了安全性。

配置客户端很简单,安装好 krb5-workstation,然后把 KDC 上的 /etc/krb5.conf 拷贝过来即可。当然还有一步,就是前面提到的为 Server 增加一个 principal,为了映像和理解的深刻,先来看看不这么做的情况。

我们使用 telnet 的 Kerberos 感知版本 krb5-telnet 来做这个实验。编辑 /etc/xinetd.d/krb5-telnet,disable = no,重启 xinted,然后用 telnet 登录(PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin)
[root@docs ~]# kinit rocky
......
[root@docs ~]# telnet -a docs.shopex.cn
Trying 192.168.0.98...
Connected to docs.shopex.cn (192.168.0.98).
Escape character is '^]'.
[ Kerberos V5 refuses authentication because telnetd: krb5_rd_req failed: No such file or directory ]
[ Kerberos V5 refuses authentication because telnetd: krb5_rd_req failed: No such file or directory ]

[root@docs ~]# telnet docs.shopex.cn
Trying 192.168.0.98...
Connected to docs.shopex.cn (192.168.0.98).
Escape character is '^]'.

docs.shopex.cn (Linux release 2.6.14.2 #1 SMP Thu Jan 11 15:39:36 EST 2007) (3)

login: rocky
Password for rocky:
Login incorrect
login: test
Password for test:
login: Client not found in Kerberos database while getting initial credentials
Last login: Thu Mar 15 18:38:58 from docs
[test@docs ~]$ exit

[root@docs ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: rocky@SHOPEX.CN

Valid starting Expires Service principal
03/15/07 18:20:52 03/16/07 18:20:49 krbtgt/SHOPEX.CN@SHOPEX.CN
03/15/07 18:37:30 03/16/07 18:20:49 host/docs.shopex.cn@SHOPEX.CN


Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached
相应的,在 /var/log/krb5kdc.log 中会看到类似的日志信息:
Mar 16 11:12:26 docs.shopex.cn krb5kdc[23244](info): TGS_REQ (7 etypes {18 17 16 23 1 3 2}) 192.168.0.98: UNKNOWN_SERVER: auth
time 1174055114, rocky@SHOPEX.CN for krbtgt/LOCALDOMAIN@SHOPEX.CN, Server not found in Kerberos database
Mar 16 11:12:26 docs.shopex.cn krb5kdc[23244](info): TGS_REQ (7 etypes {18 17 16 23 1 3 2}) 192.168.0.98: UNKNOWN_SERVER: auth
time 1174055114, rocky@SHOPEX.CN for krbtgt/LOCALDOMAIN@SHOPEX.CN, Server not found in Kerberos database

分析一下这整个过程,前面的登录都失败了,因为按照要求,Kerberos 是使用票据来进行认证的,所以不应该在登录时会提示需要密码,因为你已经在 kinit 获取票据的时候输入了密码验证信息。后面再使用 klist 查看票据时,你会发现多了一行,即 host/docs.shopex.cn@SHOPEX.CN,说明已经想 host/docs.shopex.cn@SHOPEX.CN 发送了票据,那为什么不能通过认证呢?


回顾原理,Client, Server 和 KDC 之间必须共享一个 K_session,而 Server 要取得在 TICKET 中的 K_session,它必须拥有 K_Server,但是我们什么时候创建了这个 K_Server 了呢?没有。这就是前面提到的要为这个 Server 增加一个 principal 的含义。


Additionally, the host/server@REALM principal must be in the KDC database, and its key must be stored in /etc/krb5.keytab on the server.


Kerberos applies a default authorization rule: if host H is in realm R, the Kerberos principal u@R is allowed access to the account u@H. Using this default rule implies that the system administrators are managing the correspondence between operating system (OS) usernames and Kerberos principals.


参见:"Kerberos and SSH"

那么首先增加这个 principal
sh# kadmin
kadmin: addprinc host/docs.shopex.cn
...
会提示输入以生成密码,即 K_Server。这个命令可以在 Server 上运行,也可以在 KDC 上运行。然后要将这个 Key 保存早 Server 上,这样 Server 才能使用它来解密 TICKET。
kadmin:  ktadd -k /etc/krb5.keytab host/docs.shopex.cn
Entry for principal host/docs.shopex.cn with kvno 4, encryption type ArcFour with HMAC/md5 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal host/docs.shopex.cn with kvno 4, encryption type Triple DES cbc mode with HMAC/sha1 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal host/docs.shopex.cn with kvno 4, encryption type DES with HMAC/sha1 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal host/docs.shopex.cn with kvno 4, encryption type DES cbc mode with RSA-MD5 added to keytab WRFILE:/etc/krb5.keytab.
kadmin: quit
下面看效果
[root@docs ~]# kinit
Password for rocky@SHOPEX.CN:
[root@docs ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: rocky@SHOPEX.CN

Valid starting Expires Service principal
03/16/07 09:40:34 03/17/07 09:40:28 krbtgt/SHOPEX.CN@SHOPEX.CN

Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached
[root@docs ~]# telnet -al rocky docs.shopex.cn
Trying 192.168.0.98...
Connected to docs.shopex.cn (192.168.0.98).
Escape character is '^]'.
[ Kerberos V5 accepts you as ``rocky@SHOPEX.CN'' ]
Password for rocky:
Login incorrect
login: rocky
Password for rocky:
Login incorrect

[root@docs ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: rocky@SHOPEX.CN

Valid starting Expires Service principal
03/16/07 09:40:34 03/17/07 09:40:28 krbtgt/SHOPEX.CN@SHOPEX.CN
03/16/07 09:40:59 03/17/07 09:40:28 host/docs.shopex.cn@SHOPEX.CN

Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached
相应的 /var/log/krb5kdc.log 如下
Mar 16 09:40:59 docs.shopex.cn krb5kdc[21498](info): TGS_REQ (1 etypes {1}) 192.168.0.98: ISSUE: authtime 1174052434, etypes {rep=16 tkt=23 ses=1}, rocky@SHOPEX.CN for host/docs.shopex.cn@SHOPEX.CN
Mar 16 09:40:59 docs.shopex.cn krb5kdc[21498](info): TGS_REQ (1 etypes {1}) 192.168.0.98: ISSUE: authtime 1174052434, etypes {rep=16 tkt=23 ses=1}, rocky@SHOPEX.CN for host/docs.shopex.cn@SHOPEX.CN
你可以看到"Kerberos V5 accepts you as ``rocky@SHOPEX.CN''",这说明 Kerberos 认证已经通过了,但是为什么还是不能成功的登录呢(注意,本地系统中是没有 rocky 这个帐户的!)?我们下面通过 ssh 的 pam_krb5 认证方式来详细说明这一点。

要配置 ssh 使用 Kerberos 认证,必须使用 pam_krb5 模块,对于 RedHat 系统,可以使用如下方式来配置:
sh# authconfig
sh# cat /etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required /lib/security/$ISA/pam_env.so
auth sufficient /lib/security/$ISA/pam_unix.so likeauth nullok
auth sufficient /lib/security/$ISA/pam_krb5.so use_first_pass
auth required /lib/security/$ISA/pam_deny.so

account required /lib/security/$ISA/pam_unix.so broken_shadow
account sufficient /lib/security/$ISA/pam_succeed_if.so uid < 100 quiet
account [default=bad success=ok user_unknown=ignore] /lib/security/$ISA/pam_krb5.so
account required /lib/security/$ISA/pam_permit.so

password requisite /lib/security/$ISA/pam_cracklib.so retry=3
password sufficient /lib/security/$ISA/pam_unix.so nullok use_authtok md5 shadow
password sufficient /lib/security/$ISA/pam_krb5.so use_authtok
password required /lib/security/$ISA/pam_deny.so

session required /lib/security/$ISA/pam_limits.so
session required /lib/security/$ISA/pam_unix.so
session optional /lib/security/$ISA/pam_krb5.so
然后我使用 ssh 来登录试试看。结果当然是不能成功。查看日志的情况:
Mar  8 14:18:18 docs sshd(pam_unix)[23506]: check pass; user unknown
Mar 8 14:18:18 docs sshd(pam_unix)[23506]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.0.64
Mar 8 14:18:18 docs sshd[23506]: pam_krb5[23506]: The "hosts" configuration directive is not supported with your release of Kerberos. Please check if your release supports an `extra_addresses' directive instead.
Mar 8 14:18:18 docs sshd[23506]: pam_krb5[23506]: error resolving user name 'rocky' to uid/gid pair
Mar 8 14:18:18 docs sshd[23506]: pam_krb5[23506]: error getting information about 'rocky'
可以看到,pam_unix 模块找不到用户 rocky,而 pam_krb5 也找不到,但并不是找不到用户,而是找不到与这个用户相对应的 uid/gid 匹配信息

那么做这个事情试一下:useradd rocky。然后使用 ssh 登录并监视日志的情况:

Mar 16 10:55:42 docs sshd(pam_unix)[23726]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.0.64 user=rocky
Mar 16 10:55:42 docs sshd[23726]: pam_krb5[23726]: The "hosts" configuration directive is not supported with your release of Kerberos. Please check if your release supports an `extra_addresses' directive instead.
Mar 16 10:55:42 docs sshd[23726]: pam_krb5[23726]: authentication succeeds for 'rocky' (rocky@SHOPEX.CN)
Mar 16 10:55:42 docs sshd(pam_unix)[23728]: session opened for user rocky by (uid=0)
更进一步,还可以再看看 telnet 的登录情况:
[root@docs ~]# telnet -al rocky docs.shopex.cn
Trying 192.168.0.98...
Connected to docs.shopex.cn (192.168.0.98).
Escape character is '^]'.
[ Kerberos V5 accepts you as ``rocky@SHOPEX.CN'' ]
Last login: Fri Mar 16 11:12:19 from docs
[rocky@docs ~]$ exit

[root@docs ~]# telnet -al rocky 192.168.0.98
Trying 192.168.0.98...
Connected to docs.shopex.cn (192.168.0.98).
Escape character is '^]'.
[ Kerberos V5 accepts you as ``rocky@SHOPEX.CN'' ]
Last login: Fri Mar 16 11:13:25 from docs
[rocky@docs ~]$ exit

[root@docs ~]# telnet -al rocky 127.0.0.1
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
Password for rocky:

[root@docs ~]# cat /etc/hosts
192.168.0.98 test.shopex.cn
[root@docs ~]# telnet -al rocky test.shopex.cn
Trying 192.168.0.98...
Connected to test.shopex.cn (192.168.0.98).
Escape character is '^]'.
[ Kerberos V5 accepts you as ``rocky@SHOPEX.CN'' ]
Last login: Fri Mar 16 11:13:56 from docs
[rocky@docs ~]$
由此可见,虽然我们在 Kerberos 中增加了 rocky 的 principal,但由于 pam_krb5 无法向登录的 login 程序提供用户信息的其他方面,比如 UID/GID, HOME, SHELL, EXPIRE TIME 等这些设定,所以登录会失败。

因此登录包括了两个过程,一是 Authentication,然后是 user information retrival。但是如果我在 Server 本地系统中增加 rocky 这个用户,那就意味着我要集中用户信息的管理方式无法实现。而 Kerberos 显然是不可能提供这些用户信息的。怎么办?

所以,最后归根结底,我们还是需要一个集中用户认证信息的服务。NIS 是不会采用了,那么就只能选择 LDAP 了。使用 openldap 吧。

没有评论: