# 16.9.Kerberos

Kerberos 是一种网络身份验证协议，最初由麻省理工学院（MIT）创建，旨在通过潜在的敌对网络安全地提供身份验证。Kerberos 协议使用强加密技术，使客户端和服务器能够证明其身份，而无需通过网络发送任何未加密的秘密。Kerberos 可以被描述为一种身份验证代理系统，也可以视为一种可信的第三方认证系统。在用户通过 Kerberos 进行身份验证后，他们的通信可以被加密，以确保隐私和数据完整性。

Kerberos 的唯一功能是提供对网络上用户和服务器的安全身份验证。它不提供授权或审计功能。建议将 Kerberos 与其他提供授权和审计服务的安全方法一起使用。

该协议的当前版本是第 5 版，描述于 RFC 4120 中。该协议有多个免费的实现，涵盖了广泛的操作系统。MIT 继续开发其 Kerberos 软件包。在美国，Kerberos 被作为加密产品广泛使用，并且历史上一直受到美国出口管制。在 FreeBSD 中，MITKerberos 作为 [security/krb5](https://cgit.freebsd.org/ports/tree/security/krb5/) 软件软件包和 Ports 可用。Heimdal Kerberos 实现是为了避免出口管制而在美国之外明确开发的。Heimdal Kerberos 发行版已包含在 FreeBSD 的基本安装中，另一个具有更多可配置选项的发行版可作为 [security/heimdal](https://cgit.freebsd.org/ports/tree/security/heimdal/) 在 Ports 中使用。

在 Kerberos 中，用户和服务被称为“主体”（principals），它们包含在一个名为“领域”（realm）的管理分组中。一个典型的用户主体形式为 `user@REALM`（领域通常使用大写字母）。

本节提供了如何使用 FreeBSD 中包含的 Heimdal 发行版设置 Kerberos 的指南。

为了演示 Kerberos 安装，命名空间将如下所示：

* DNS 域（区域）将为 `example.org`。
* Kerberos 领域将为 `EXAMPLE.ORG`。

> **注意**
>
> 在设置 Kerberos 时使用真实的域名，即使它仅在内部运行。这可以避免 DNS 问题并确保与其他 Kerberos 领域的互操作性

## 16.9.1. 设置 Heimdal KDC

密钥分发中心（KDC）是 Kerberos 提供的集中式身份验证服务，是系统中的“可信第三方”。它是发放 Kerberos 票证的计算机，客户端用它来进行服务器身份验证。由于 KDC 被所有其他计算机视为可信，因此它具有更高的安全性。应限制对 KDC 的直接访问。

虽然运行 KDC 需要的计算资源较少，但为了安全原因，建议使用仅作为 KDC 运行的专用机器。

首先，按如下方式安装 [security/heimdal](https://cgit.freebsd.org/ports/tree/security/heimdal/) 软件包：

```sh
# pkg install heimdal
```

接下来，使用 `sysrc` 更新 **/etc/rc.conf**：

```sh
# sysrc kdc_enable=yes
# sysrc kadmind_enable=yes
```

接下来，按如下方式编辑 **/etc/krb5.conf**：

```ini
[libdefaults]
    default_realm = EXAMPLE.ORG
[realms]
    EXAMPLE.ORG = {
	kdc = kerberos.example.org
	admin_server = kerberos.example.org
    }
[domain_realm]
    .example.org = EXAMPLE.ORG
```

在这个例子中，KDC 将使用完全限定的主机名 `kerberos.example.org`。KDC 的主机名必须在 DNS 中是可解析的。

Kerberos 还可以使用 DNS 来定位 KDC，而不是在 **/etc/krb5.conf** 中使用 `[realms]` 部分。对于拥有自己 DNS 服务器的大型组织，上面的示例可以简化为：

```ini
[libdefaults]
      default_realm = EXAMPLE.ORG
[domain_realm]
    .example.org = EXAMPLE.ORG
```

并在 `example.org` 区域文件中包含以下行：

```ini
_kerberos._udp      IN  SRV     01 00 88 kerberos.example.org.
_kerberos._tcp      IN  SRV     01 00 88 kerberos.example.org.
_kpasswd._udp       IN  SRV     01 00 464 kerberos.example.org.
_kerberos-adm._tcp  IN  SRV     01 00 749 kerberos.example.org.
_kerberos           IN  TXT     EXAMPLE.ORG
```

> **注意**
>
> 为了让客户端能够找到 Kerberos 服务，它们 *必须* 拥有完全配置的 **/etc/krb5.conf**，或者至少配置了 **/etc/krb5.conf** 和正确配置的 DNS 服务器。

接下来，创建包含所有主体（用户和主机）密钥的 Kerberos 数据库，这些密钥使用主密码进行加密。无需记住此密码，因为它将存储在 **/var/heimdal/m-key** 中；为了这个目的，使用一个 45 字符的随机密码是合理的。要创建主密钥，请运行 `kstash` 并输入密码：

```sh
# kstash
```

输出应类似于以下内容：

```sh
Master key: xxxxxxxxxxxxxxxxxxxxxxx
Verifying password - Master key: xxxxxxxxxxxxxxxxxxxxxxx
```

创建主密钥后，应初始化数据库。Kerberos 管理工具 [kadmin(8)](https://man.freebsd.org/cgi/man.cgi?query=kadmin\&sektion=8\&format=html) 可以在 KDC 上直接对数据库操作，而无需使用 [kadmind(8)](https://man.freebsd.org/cgi/man.cgi?query=kadmind\&sektion=8\&format=html) 网络服务，使用 `kadmin -l` 运行。这解决了在数据库创建之前尝试连接到数据库的问题。在 `kadmin` 提示符下，使用 `init` 创建领域的初始数据库：

```sh
# kadmin -l
kadmin> init EXAMPLE.ORG
Realm max ticket life [unlimited]:
```

最后，在 `kadmin` 中使用 `add` 创建第一个主体。暂时保留主体的默认选项，因为这些选项稍后可以通过 `modify` 修改。输入 `?` 可以查看可用的选项。

```sh
kadmin> add tillman
```

输出应类似于以下内容：

```sh
Max ticket life [unlimited]:
Max renewable life [unlimited]:
Principal expiration time [never]:
Password expiration time [never]:
Attributes []:
Password: xxxxxxxx
Verifying password - Password: xxxxxxxx
```

接下来，通过运行以下命令启动 KDC 服务：

```sh
# service kdc start
# service kadmind start
```

虽然此时不会有 Kerberos 守护进程在运行，但可以通过获取刚刚创建的主体的票证来确认 KDC 是否正常工作：

```sh
% kinit tillman
```

输出应类似于以下内容：

```sh
tillman@EXAMPLE.ORG's Password:
```

使用 `klist` 确认是否成功获取票证：

```sh
% klist
```

输出应类似于以下内容：

```sh
Credentials cache: FILE:/tmp/krb5cc_1001
	Principal: tillman@EXAMPLE.ORG

  Issued                Expires               Principal
Aug 27 15:37:58 2013  Aug 28 01:37:58 2013  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
```

测试完成后，可以销毁临时票证：

```sh
% kdestroy
```

## 16.9.2. 配置服务器以使用 Kerberos

配置服务器以使用 Kerberos 身份验证的第一步是确保 **/etc/krb5.conf** 文件正确配置。可以直接使用 KDC 中的版本，也可以在新系统上重新生成该文件。

接下来，在服务器上创建 **/etc/krb5.keytab** 文件。这是将服务“Kerberize”的关键步骤——它对应于生成一个在服务与 KDC 之间共享的密钥。该密钥是加密密钥，存储在一个“keytab”文件中。keytab 包含服务器的主机密钥，允许服务器和 KDC 验证彼此的身份。必须以安全的方式将其传输到服务器，因为如果密钥公开，服务器的安全性将被破坏。通常，**keytab** 是在管理员的受信机器上使用 `kadmin` 生成的，然后通过安全的方式传输到服务器，例如使用 [scp(1)](https://man.freebsd.org/cgi/man.cgi?query=scp\&sektion=1\&format=html)；如果与所需的安全策略一致，也可以直接在服务器上创建。非常重要的是，必须以安全的方式将 keytab 传输到服务器：如果密钥被其他方知道，该方可以冒充任何用户访问服务器！直接在服务器上使用 `kadmin` 很方便，因为 KDC 数据库中的主机主体条目也是通过 `kadmin` 创建的。

当然，`kadmin` 是一个 Kerberos 服务；需要 Kerberos 票证才能对网络服务进行身份验证，但为了确保运行 `kadmin` 的用户确实在场（并且他们的会话没有被劫持），`kadmin` 会提示输入密码以获取新票证。对 `kadmin` 服务进行身份验证的主体必须被允许使用 `kadmin` 接口，这在 **/var/heimdal/kadmind.acl** 中进行了指定。有关设计访问控制列表的详细信息，请参阅 `info heimdal` 中的“远程管理”部分。管理员也可以通过本地控制台或 [ssh(1)](https://man.freebsd.org/cgi/man.cgi?query=ssh\&sektion=1\&format=html) 安全地连接到 KDC，并使用 `kadmin -l` 本地执行管理操作。

安装 **/etc/krb5.conf** 后，使用 `add --random-key` 在 `kadmin` 中添加服务器的主机主体到数据库，但这不会将主机主体密钥提取到 keytab 中。要生成 keytab，可以使用 `ext` 将服务器的主机主体密钥提取到其自己的 keytab 中：

```sh
# kadmin
```

输出应类似于以下内容：

```sh
kadmin> add --random-key host/myserver.example.org
Max ticket life [unlimited]:
Max renewable life [unlimited]:
Principal expiration time [never]:
Password expiration time [never]:
Attributes []:
kadmin> ext_keytab host/myserver.example.org
kadmin> exit
```

请注意，`ext_keytab` 默认将提取的密钥存储在 **/etc/krb5.keytab** 中。当在正在进行 Kerberize 操作的服务器上运行时，这样做是可以的，但如果在其他地方提取 keytab，则应使用 `--keytab <path/to/file>` 参数：

```sh
# kadmin
```

输出应类似于以下内容：

```sh
kadmin> ext_keytab --keytab=/tmp/example.keytab host/myserver.example.org
kadmin> exit
```

然后，可以通过 [scp(1)](https://man.freebsd.org/cgi/man.cgi?query=scp\&sektion=1\&format=html) 或可移动媒体将 keytab 安全地复制到服务器。务必指定一个非默认的 keytab 名称，以避免将不必要的密钥插入到系统的 keytab 中。

此时，服务器可以使用其共享密钥（存储在 **krb5.keytab** 中）读取来自 KDC 的加密消息。它现在准备启用使用 Kerberos 的服务。最常见的此类服务之一是 [sshd(8)](https://man.freebsd.org/cgi/man.cgi?query=sshd\&sektion=8\&format=html)，它通过 GSS-API 支持 Kerberos。在 **/etc/ssh/sshd\_config** 中，添加以下行：

```ini
GSSAPIAuthentication yes
```

更改此配置后，必须重启 [sshd(8)](https://man.freebsd.org/cgi/man.cgi?query=sshd\&sektion=8\&format=html) 以使新配置生效：`service sshd restart`。

## 16.9.3. 配置客户端以使用 Kerberos

与服务器一样，客户端也需要在 **/etc/krb5.conf** 中进行配置。可以将该文件复制到客户端（以安全方式）或根据需要重新输入。

通过使用 `kinit`、`klist` 和 `kdestroy` 在客户端进行测试，以获取、显示并删除一个现有主体的票证。Kerberos 应用程序也应能够连接到启用 Kerberos 的服务器。如果这无法正常工作，但获取票证成功，则问题可能出在服务器，而非客户端或 KDC。对于 Kerberized 的 [ssh(1)](https://man.freebsd.org/cgi/man.cgi?query=ssh\&sektion=1\&format=html)，默认情况下 GSS-API 是禁用的，因此可以使用 `ssh -o GSSAPIAuthentication=yes <hostname>` 来进行测试。

在测试 Kerberized 应用程序时，可以尝试使用 `tcpdump` 等数据包嗅探工具，以确认没有敏感信息以明文形式传输。

各种 Kerberos 客户端应用程序可供使用。随着一个桥接机制的出现，使得使用 SASL 进行身份验证的应用程序也能使用 GSS-API 机制，许多客户端应用程序都可以使用 Kerberos 进行身份验证，从 Jabber 客户端到 IMAP 客户端。

在一个域内，用户的 Kerberos 主体通常会映射到本地用户账户。有时，需要允许没有匹配 Kerberos 主体的本地用户账户访问。例如，`tillman@EXAMPLE.ORG` 可能需要访问本地用户账户 `webdevelopers`。其他主体也可能需要访问该本地账户。

**.k5login** 和 **.k5users** 文件可以放在用户的主目录中，用来解决这个问题。例如，如果以下的 **.k5login** 文件放在 `webdevelopers` 用户的主目录中，列出的两个主体将可以访问该账户，而无需共享密码：

```ini
tillman@example.org
jdoe@example.org
```

有关 **.k5users** 的更多信息，请参阅 [ksu(1)](https://man.freebsd.org/cgi/man.cgi?query=ksu\&sektion=1\&format=html)。

## 16.9.4. MIT 和 Heimdal 的差异

MIT 和 Heimdal 实现之间的主要区别在于 `kadmin` 有一组不同的命令，并使用不同的协议。如果 KDC 是 MIT 版本的，则不能使用 Heimdal 版本的 `kadmin` 远程管理 KDC，反之亦然。

客户端应用程序在执行相同任务时，可能会使用略有不同的命令行选项。建议参考 <http://web.mit.edu/Kerberos/www/> 中的说明。注意路径问题：MIT Port 默认安装到 **/usr/local/**，如果 `PATH` 列出系统目录在前，FreeBSD 系统应用程序将代替 MIT 版本运行。

当在 FreeBSD 上使用 MIT Kerberos 作为 KDC 时，请执行以下命令，将所需的配置添加到 **/etc/rc.conf** 中：

```sh
# sysrc kdc_program="/usr/local/sbin/krb5kdc"
# sysrc kadmind_program="/usr/local/sbin/kadmind"
# sysrc kdc_flags=""
# sysrc kdc_enable="YES"
# sysrc kadmind_enable="YES"
```

## 16.9.5. Kerberos 配置技巧、窍门与故障排除

在配置和故障排除 Kerberos 时，请牢记以下几点：

* 使用 Heimdal 或 MITKerberos 的 Port 时，确保 `PATH` 列表中的客户端应用程序版本优先于系统版本。
* 如果领域中的所有计算机的时间设置不同步，身份验证可能会失败。[“使用 NTP 进行时钟同步”](https://docs.freebsd.org/en/books/handbook/network-servers/#network-ntp) 介绍了如何使用 NTP 同步时钟。
* 如果更改了主机名，则必须更改 `host/` 主体并更新 keytab。对于像 Apache 的 [www/mod\_auth\_kerb](https://cgit.freebsd.org/ports/tree/www/mod_auth_kerb/) 使用的 `HTTP/` 主体等特殊 keytab 条目也适用此规则。
* 领域中的所有主机必须能够在 DNS 中进行正向和反向解析，或者至少存在于 **/etc/hosts** 中。CNAME 记录可以使用，但 A 和 PTR 记录必须正确并已设置。无法解析主机时，错误信息并不直观：`Kerberos5 拒绝认证，因为读取请求失败：未找到密钥表条目`。
* 一些作为客户端连接到 KDC 的操作系统未将 `ksu` 的权限设置为 setuid `root`。这意味着 `ksu` 无法正常工作。这是权限问题，而不是 KDC 错误。
* 使用 MITKerberos 时，要允许主体的票证生命周期超过默认的十小时生命周期，请在 [kadmin(8)](https://man.freebsd.org/cgi/man.cgi?query=kadmin\&sektion=8\&format=html) 提示符下使用 `modify_principal` 修改相关主体和 `krbtgt` 主体的 `maxlife`。然后，主体可以使用 `kinit -l` 请求具有更长生命周期的票证。
* 在 KDC 上运行数据包嗅探器来辅助故障排除时，运行 `kinit` 从工作站启动，Ticket Granting Ticket (TGT) 会立即发送，甚至在输入密码之前。这是因为 Kerberos 服务器会自由地向任何未授权的请求发送 TGT。然而，每个 TGT 都会使用从用户密码派生的密钥加密。当用户输入密码时，密码并不会发送到 KDC，而是用于解密 `kinit` 已经获取的 TGT。如果解密过程导致有效票证和有效时间戳，则说明用户拥有有效的 Kerberos 凭据。这些凭据包括用于建立与 Kerberos 服务器之间安全通信的会话密钥，以及实际的 TGT，该 TGT 使用 Kerberos 服务器自身的密钥加密。第二层加密允许 Kerberos 服务器验证每个 TGT 的真实性。
* 主机主体可以有更长的票证生命周期。如果用户主体的生命周期为一周，但连接的主机的生命周期为九小时，则用户缓存将包含过期的主机主体，票证缓存将无法按预期工作。
* 设置 **krb5.dict** 以防止使用特定坏密码（如 [kadmind(8)](https://man.freebsd.org/cgi/man.cgi?query=kadmind\&sektion=8\&format=html) 中所述）时，请记住它仅适用于已分配密码策略的主体。**krb5.dict** 中使用的格式为每行一个字符串。创建指向 **/usr/share/dict/words** 的符号链接可能会有所帮助。

## 16.9.6. 缓解 Kerberos 限制

由于 Kerberos 是一种“全有或全无”的方法，网络上的每个启用的服务必须要么修改以与 Kerberos 一起工作，要么采取其他措施来防止网络攻击。这是为了防止用户凭证被窃取并被重新使用。一个例子是，当所有远程 shell 启用了 Kerberos，但非 Kerberized 的 POP3 邮件服务器仍以明文发送密码。

KDC 是单点故障。根据设计，KDC 必须像其主密码数据库一样安全。KDC 上不应该运行任何其他服务，并且应该物理上保持安全。危险性很高，因为 Kerberos 将所有密码加密存储，并使用相同的主密钥，而该密钥作为文件存储在 KDC 上。

被攻破的主密钥并不像人们想象的那么糟糕。主密钥仅用于加密 Kerberos 数据库，并作为随机数生成器的种子。只要对 KDC 的访问是安全的，攻击者无法利用主密钥做太多事情。

如果 KDC 无法访问，网络服务将无法使用，因为无法执行身份验证。可以通过设置一个主 KDC 和一个或多个从 KDC，以及通过小心实现使用 PAM 进行的备用身份验证来缓解这一问题。

Kerberos 允许用户、主机和服务彼此之间进行身份验证，但没有机制可以验证 KDC 对用户、主机或服务的身份。这意味着一个被篡改的 `kinit` 可能记录所有用户名和密码。像 [security/tripwire](https://cgit.freebsd.org/ports/tree/security/tripwire/) 这样的文件系统完整性检查工具可以缓解这个问题。

## 16.9.7. 资源与进一步的信息

* [Kerberos 常见问题解答](http://www.faqs.org/faqs/Kerberos-faq/general/preamble.html)
* [设计一个认证系统：四幕对话](http://web.mit.edu/Kerberos/www/dialogue.html)
* [RFC 4120, Kerberos 网络认证服务（V5）](https://www.ietf.org/rfc/rfc4120.txt)
* [MIT Kerberos 官方主页](http://web.mit.edu/Kerberos/www/)
* [Heimdal Kerberos 项目 Wiki 页面](https://github.com/heimdal/heimdal/wiki)
