Fernet - 常见问题解答

自 Kilo 版本发布 Fernet 令牌格式以来,以下问题被周期性地提出。

什么是 Fernet 令牌?

Fernet 令牌是一种承载令牌,代表用户身份验证。Fernet 令牌在 MessagePacked 负载中包含有限的身份和授权数据。然后,该负载被包装为 Fernet 消息进行传输,Fernet 提供在 URL 和标头中使用所需的 Web 安全特性。Fernet 令牌内部的数据使用对称加密密钥或 Fernet 密钥进行保护。

什么是 Fernet 密钥?

Fernet 密钥用于加密和解密 Fernet 令牌。每个密钥实际上由两个较小的密钥组成:一个 128 位 AES 加密密钥和一个 128 位 SHA256 HMAC 签名密钥。这些密钥存储在一个密钥存储库中,Keystone 将其传递给处理令牌加密和解密的库。

密钥有哪些不同的类型?

Keystone 需要密钥存储库才能创建 Fernet 令牌。这些密钥用于加密和解密构成令牌负载的信息。存储库中的每个密钥可以具有三种状态之一。密钥的状态决定了 Keystone 如何使用密钥处理 Fernet 令牌。不同的类型如下

主密钥

密钥存储库中始终只有一个主密钥。主密钥允许加密和解密令牌。此密钥始终命名为存储库中的最高索引。

辅助密钥

辅助密钥曾经是主密钥,但已被另一个主密钥降级。它仅允许解密令牌。由于它曾经是主密钥,因此其在密钥存储库中的存在是合理的。Keystone 需要能够解密使用旧主密钥创建的令牌。

暂定密钥

暂定密钥是一种特殊的密钥,与辅助密钥有一些相似之处。存储库中始终只有一个暂定密钥,并且必须存在。与辅助密钥一样,暂定密钥具有解密令牌的能力。与辅助密钥不同,暂定密钥从未成为主密钥。事实上,它们是相反的,因为暂定密钥将始终是下一个主密钥。这有助于阐明名称,因为它们是下一个要暂定为主要密钥。

那么,暂定密钥如何帮助我,为什么我应该关心它?

Fernet 密钥具有自然生命周期。每个密钥都从暂定密钥开始,晋升为主要密钥,然后降级为辅助密钥。只能使用主密钥加密新令牌。辅助密钥和暂定密钥绝不用于加密令牌。暂定密钥是一种特殊的密钥,考虑到事件的顺序和每种密钥类型的属性。暂定密钥是存储库中唯一尚未有机会加密任何令牌的密钥,但它仍然允许解密令牌。作为操作员,这使您有机会在一个 Keystone 节点上执行密钥轮换,并在一段时间内分发新的密钥集。这不需要在极短的时间内进行分发。使用主密钥加密的令牌可以在其他节点上解密和验证,只要该密钥仍然是暂定的。

我应该将密钥存储库放在哪里?

密钥存储库使用 Keystone 配置文件中的 key_repository 选项指定。Keystone 进程应该能够读取和写入此位置,但否则应将其保密。目前,Keystone 仅支持基于文件的密钥存储库。

[fernet_tokens]
key_repository = /etc/keystone/fernet-keys/

Fernet 令牌是否仍然会过期?

是的,Fernet 令牌可以像任何其他 Keystone 令牌格式一样过期。

我应该选择 Fernet 令牌而不是 UUID 令牌的原因是什么?

尽管 Fernet 令牌的操作方式与 UUID 令牌非常相似,但它们不需要持久性或利用配置的令牌持久性驱动程序。Keystone 令牌数据库不再因身份验证而膨胀。使用 Fernet 令牌不再需要从令牌数据库中删除过期的令牌。由于 Fernet 令牌不需要持久性,因此它们不需要复制。只要每个 Keystone 节点共享相同的密钥存储库,Fernet 令牌就可以立即在节点之间创建和验证。

我应该选择 Fernet 令牌而不是 PKI 或 PKIZ 令牌的原因是什么?

使用 Fernet 代替 PKI 和 PKIZ 的论点与 UUID 相同,此外 Fernet 令牌比 PKI 和 PKIZ 令牌小得多。PKI 和 PKIZ 令牌仍然需要持久存储,并且有时由于其大小而可能导致问题。切换到 Fernet 可以缓解此问题,因为 Fernet 令牌保持在 250 字节以下。PKI 和 PKIZ 令牌通常超过 1600 字节。PKI 或 PKIZ 令牌的长度取决于部署的大小。更大的服务目录将导致更长的令牌长度。Fernet 令牌不存在这种模式,因为加密负载的内容保持在最低限度。

我应该每次轮换都从同一个 Keystone 节点轮换和分发密钥吗?

不,但轮换和分发之间的关系应该是同步的。一旦在一个 Keystone 节点上轮换密钥,就应该将该节点上的密钥存储库分发到集群的其余部分。一旦确认每个节点都具有相同的密钥存储库状态,就可以从集群中的任何其他节点轮换和分发。

如果轮换和分发不同步,部署中的单个 Keystone 节点将使用其他节点没有暂定密钥的主密钥创建令牌。这将导致从一个 Keystone 节点生成的令牌在其他 Keystone 节点上验证失败。

如何将新的 Keystone 节点添加到部署中?

用于创建 Fernet 令牌的密钥应被视为像超级秘密配置文件一样的机密文件,类似于 SSL 秘密密钥。在允许节点加入现有集群之前,发行和验证令牌,它应该具有与集群中其余节点相同的密钥存储库。

我应该如何进行密钥分发?

请记住,密钥分发仅需要在多节点 Keystone 部署中进行。如果您的部署中只有一个 Keystone 节点提供请求,则不需要密钥分发。

密钥分发最好从部署当前的配置管理系统入手。由于并非所有部署都使用相同的配置管理系统,因此有必要探索围绕已经可用于管理密钥的选项,同时牢记密钥的保密性。许多配置管理工具可以利用类似 rsync 的工具来管理密钥分发。

密钥轮换是一个操作,它将当前暂定密钥提升为主要密钥,创建一个新的暂定密钥,并删除旧的辅助密钥。最好在一个节点上执行此操作,并在分发密钥存储库到集群的其余部分之前验证轮换是否成功。暂定密钥的概念打破了密钥轮换和密钥分发必须在单个步骤中完成的期望。有了暂定密钥,我们有时间检查新的密钥存储库,然后再与集群的其余部分同步状态。密钥分发应该是一个可以连续运行直到成功的操作。以下内容可能有助于说明密钥轮换和密钥分发之间的隔离。

  1. 确保部署中的所有 Keystone 节点都具有相同的密钥存储库。

  2. 选择集群中的一个 Keystone 节点进行轮换。

  3. 轮换密钥。

    1. 成功了吗?

      1. 如果没有,请调查您轮换密钥的特定 Keystone 节点的问题。Fernet 密钥很小,轮换操作也很简单。密钥轮换中很少出错。用户可能没有权限将新密钥写入密钥存储库。来自 keystone-manage fernet_rotate 的日志输出应该提供有关特定错误的更多信息。

      2. 如果成功,您应该看到一个新的暂定密钥。旧暂定密钥应该是新的主密钥。根据 max_active_keys 限制,您可能会删除一些辅助密钥。此时,您轮换的节点将使用所有其他节点应将其作为暂定密钥的主密钥创建 Fernet 令牌。这就是我们检查步骤一中所有密钥存储库状态的原因。集群中的所有其他节点都应该能够解密使用新主密钥创建的令牌。此时,我们准备好分发新的密钥集。

  4. 分发新的密钥存储库。

    1. 成功了吗?

      1. 如果成功,您应该能够确认集群中的所有节点都具有在步骤 3 中引入的相同的密钥存储库。集群中的所有节点将使用在步骤 3 中提升的主密钥创建令牌。不需要采取进一步的操作,直到下一次计划的密钥轮换。

      2. 如果失败,请再次尝试分发。请记住,我们已经轮换了存储库,此时执行另一个轮换将导致无法在某些主机上验证的令牌。具体来说,是那些没有获得最新密钥集的那些主机。您应该能够分发密钥,直到成功为止。如果某些节点在同步时遇到问题,可能是权限或网络问题,在后续轮换之前应解决这些问题。

我应该保留密钥多长时间?

Keystone 创建的 Fernet 令牌仅与创建它们的密钥一样安全。使用暂定密钥,密钥轮换的代价很低,允许您在安全性方面犯错,并每周、每天甚至每小时轮换一次。最终,这应该比攻击者破解 AES256 密钥和 SHA256 HMAC 所需的时间更短。

Fernet 令牌仍然是承载令牌吗?

是的,它们遵循与 UUID 令牌完全相同的验证路径,不同之处在于它们是从后端写入和读取的。如果有人破坏了您的 Fernet 令牌,他们将拥有执行您被允许执行的所有操作的权限。

如果我需要撤销所有令牌怎么办?

要使 Keystone 发出的每个令牌失效并重新开始,请删除当前的密钥存储库,创建一个新的密钥集,并将其分发到集群中的所有节点。这将使 Keystone 发出的每个令牌都无效,无论令牌是否实际过期。当客户端尝试重新进行身份验证时,新的令牌将使用新的 Fernet 密钥创建。

如果攻击者破坏了部署中的 Fernet 密钥,他们能做什么?

如果密钥存储库中的任何密钥被破坏,攻击者将能够构建他们自己的令牌。如果他们知道项目中的管理员的 ID,他们可以为该项目生成管理员令牌。他们将能够生成自己的令牌,直到被破坏的密钥从存储库中删除。

我轮换了密钥,现在令牌过早失效,我做了什么?

使用 Fernet 令牌需要了解令牌过期和密钥生命周期。您不希望轮换得太频繁,以至于仍然可能需要解密未过期的令牌的辅助密钥被删除。如果发生这种情况,您将无法解密令牌,因为用于加密它的密钥现在已消失。仅删除您知道不再用于加密或解密令牌的密钥。

例如,您的令牌有效期为 24 小时,我们希望每六小时轮换一次密钥。我们需要确保周一上午 8:00 创建的令牌在周二上午 7:00 仍然有效,假设它们没有过早撤销。为了实现这一点,我们希望在 Keystone 配置文件中设置 max_active_keys=6。这将允许我们保存可能仍然需要验证以前的令牌的所有密钥,但将密钥存储库限制为仅需要的密钥。

部署的 max_active_keys 数量可以通过将令牌的生命周期(以小时为单位)除以轮换频率(以小时为单位)并加上 2 来确定。

token_expiration = 24
rotation_frequency = 6
max_active_keys = (token_expiration / rotation_frequency) + 2

添加两个密钥的原因是包含暂定密钥和一个缓冲密钥。

注意

如果需要验证过期的令牌(例如,当服务配置为使用 ServiceToken 身份验证时),还应考虑 [token] 配置部分中的 allow_expired_window 选项的值,因此计算 max_active_keys 的公式是

max_active_keys = ((token_expiration + allow_expired_window) / rotation_frequency) + 2

这可以基于前面的示例说明。我们最初在周一上午 6:00 设置密钥存储库,初始状态如下

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 1    (primary key)

在上午 12:00 再次轮换密钥后,所有在上午 6:00 到 11:59 之间创建的令牌都使用密钥 1 加密。

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 1    (secondary key)
-rw------- 1 keystone keystone   44 2    (primary key)

我们仍然能够验证在 6:00 - 11:59 AM 之间创建的令牌,因为 1 密钥仍然存在作为辅助密钥。所有在中午 12:00 之后发出的令牌都将使用密钥 2 加密。下午 6:00 我们进行下一次轮换,结果是

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 1    (secondary key)
-rw------- 1 keystone keystone   44 2    (secondary key)
-rw------- 1 keystone keystone   44 3    (primary key)

仍然可以验证从上午 6:00 到下午 5:59 发出的令牌,因为密钥 12 存在作为辅助密钥。每个在晚上 11:59 之前发出的令牌都将使用密钥 3 加密,并在午夜 12:00 进行下一次轮换

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 1    (secondary key)
-rw------- 1 keystone keystone   44 2    (secondary key)
-rw------- 1 keystone keystone   44 3    (secondary key)
-rw------- 1 keystone keystone   44 4    (primary key)

就像之前一样,我们仍然可以验证从前一天上午 6:00 到今天上午 5:59 发出的令牌,因为密钥 1 - 4 存在。在上午 6:00,前一天发出的令牌将开始过期,并且我们将进行下一次计划轮换。

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 1    (secondary key)
-rw------- 1 keystone keystone   44 2    (secondary key)
-rw------- 1 keystone keystone   44 3    (secondary key)
-rw------- 1 keystone keystone   44 4    (secondary key)
-rw------- 1 keystone keystone   44 5    (primary key)

令牌会在上午 6:00 后自然过期,但我们无法在下一次轮换之前删除密钥 1,因为它加密了前一天上午 6:00 到中午 12:00 之间发出的所有令牌。一旦我们进行下一次轮换,即中午 12:00,密钥 1 将从存储库中删除。

$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone   44 0    (staged key)
-rw------- 1 keystone keystone   44 2    (secondary key)
-rw------- 1 keystone keystone   44 3    (secondary key)
-rw------- 1 keystone keystone   44 4    (secondary key)
-rw------- 1 keystone keystone   44 5    (secondary key)
-rw------- 1 keystone keystone   44 6    (primary key)

如果 Keystone 收到一个在前一天上午 6:00 到中午 12:00 之间创建的令牌,并使用密钥 1 加密,那么该令牌将无效,因为它已经过期。这使得我们可以从存储库中删除密钥 1,而不会产生负面的验证副作用。