16.3. Tang 服务器加密密钥管理

重新创建加密密钥的加密机制基于节点上存储的 blinded key 以及相关 Tang 服务器的私钥。为防止攻击者同时获得 Tang 服务器私钥和节点的加密磁盘的可能性,建议定期重新密钥。

您必须对每个节点执行重新密钥操作,然后才能从 Tang 服务器中删除旧密钥。以下小节提供了重新密钥和删除旧密钥的步骤。

16.3.1. 为 Tang 服务器备份密钥

Tang 服务器使用 /usr/libexec/tangd-keygen 生成新密钥,并将其默认存储在 /var/db/tang 目录中。要在失败时恢复 Tang 服务器,请备份这个目录。密钥是敏感的,因为它们能够对已使用它们的所有主机执行引导磁盘解密,因此必须相应地保护密钥。

流程

  • /var/db/tang 目录中的备份密钥复制到可以恢复密钥的 temp 目录。

16.3.2. 为 Tang 服务器恢复密钥

您可以通过从备份访问密钥来恢复 Tang 服务器的密钥。

流程

  • 将备份文件夹中的密钥恢复到 /var/db/tang/ 目录。

    当 Tang 服务器启动时,它会公告并使用这些恢复的密钥。

16.3.3. 重新密钥 (Rekeying) Tang 服务器

此流程使用一组包含三个 Tang 服务器,各自具有唯一密钥作为一个示例。

使用冗余 Tang 服务器可减少节点无法自动引导的几率。

重新加密 Tang 服务器和所有关联的 NBDE 加密节点是一个三个步骤。

先决条件

  • 在一个或多个节点上安装正常工作的网络级磁盘加密 (NBDE) 安装。

流程

  1. 生成新的 Tang 服务器密钥。
  2. 重新密钥所有 NBDE 加密节点,以便它们使用新密钥。
  3. 删除旧的 Tang 服务器密钥。

    注意

    在所有 NBDE 加密的节点完成重新密钥前删除旧密钥会导致这些节点过度依赖于任何其他配置的 Tang 服务器。

图 16.1. 重新密钥 Tang 服务器的工作流示例

重新密钥 Tang 服务器

16.3.3.1. 生成新的 Tang 服务器密钥

先决条件

  • 运行 Tang 服务器的 Linux 计算机上的 root shell。
  • 为了便于验证 Tang 服务器密钥轮转,请使用旧密钥加密小测试文件:

    # echo plaintext | clevis encrypt tang '{"url":"http://localhost:7500”}' -y >/tmp/encrypted.oldkey
  • 验证加密成功,并且可以解密文件来生成相同的字符串的明文

    # clevis decrypt </tmp/encrypted.oldkey

流程

  1. 找到并访问存储 Tang 服务器密钥的目录。这通常是 /var/db/tang 目录。检查当前公告的密钥 thumbprint:

    # tang-show-keys 7500

    输出示例

    36AHjNH3NZDSnlONLz1-V4ie6t8

  2. 输入 Tang 服务器密钥目录:

    # cd /var/db/tang/
  3. 列出当前的 Tang 服务器密钥:

    # ls -A1

    输出示例

    36AHjNH3NZDSnlONLz1-V4ie6t8.jwk
    gJZiNPMLRBnyo_ZKfK4_5SrnHYo.jwk

    在正常的 Tang 服务器操作过程中,此目录中有两个 .jwk 文件:一个用于签名和验证,另一个用于密钥生成。

  4. 禁用旧密钥的公告:

    # for key in *.jwk; do \
      mv -- "$key" ".$key"; \
    done

    设置 Network-Bound Disk 加密 (NBDE) 或请求密钥的新客户端将不再看到旧密钥。现有的客户端仍然可以访问和使用旧密钥,直到它们被删除为止。Tang 服务器读取但不会公告存储在 UNIX 隐藏文件中的密钥,这些文件以 . 字符开头。

  5. 生成新密钥:

    # /usr/libexec/tangd-keygen /var/db/tang
  6. 列出当前的 Tang 服务器密钥,以验证旧密钥不再公告,因为它们现在是隐藏的文件,并存在新密钥:

    # ls -A1

    输出示例

    .36AHjNH3NZDSnlONLz1-V4ie6t8.jwk
    .gJZiNPMLRBnyo_ZKfK4_5SrnHYo.jwk
    Bp8XjITceWSN_7XFfW7WfJDTomE.jwk
    WOjQYkyK7DxY_T5pMncMO5w0f6E.jwk

    Tang 自动公告新密钥。

    注意

    较新的 Tang 服务器安装包括帮助程序 /usr/libexec/tangd-rotate-keys 目录,负责同时禁用公告并生成新密钥。

  7. 如果您在共享相同关键资料的负载均衡器后面运行多个 Tang 服务器,请确保在继续之前,在此进行的更改会正确同步整个服务器集。

验证

  1. 验证 Tang 服务器是否在广播新密钥,而不是公告旧密钥:

    # tang-show-keys 7500

    输出示例

    WOjQYkyK7DxY_T5pMncMO5w0f6E

  2. 验证旧密钥(尽管未公告)仍然可用于解密请求:

    # clevis decrypt </tmp/encrypted.oldkey

16.3.3.2. 重新密钥所有 NBDE 节点

您可以使用 DaemonSet 对象来更新远程集群中的所有节点的密钥,而不会给远程集群造成任何停机时间。

注意

如果在重新密钥过程中节点断电,则可能无法引导,且必须通过 Red Hat Advanced Cluster Management (RHACM) 或 GitOps 管道重新部署。

先决条件

  • cluster-admin 对具有 Network-Bound Disk Encryption (NBDE) 节点的所有集群进行访问。
  • 所有 Tang 服务器都必须可供每个 NBDE 节点进行 rekeying 进行访问,即使 Tang 服务器的密钥没有改变。
  • 获取每个 Tang 服务器的 Tang 服务器 URL 和密钥 thumbprint。

流程

  1. 根据以下模板创建 DaemonSet 对象。此模板设置三台冗余 Tang 服务器,但可轻松适应其他情况。更改 NEW_TANG_PIN 环境中的 Tang 服务器 URL 和 thumbprints 以适合您的环境:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: tang-rekey
      namespace: openshift-machine-config-operator
    spec:
      selector:
        matchLabels:
          name: tang-rekey
      template:
        metadata:
          labels:
            name: tang-rekey
        spec:
          containers:
          - name: tang-rekey
            image: registry.access.redhat.com/ubi8/ubi-minimal:8.4
            imagePullPolicy: IfNotPresent
            command:
            - "/sbin/chroot"
            - "/host"
            - "/bin/bash"
            - "-ec"
            args:
            - |
              rm -f /tmp/rekey-complete || true
              echo "Current tang pin:"
              clevis-luks-list -d $ROOT_DEV -s 1
              echo "Applying new tang pin: $NEW_TANG_PIN"
              clevis-luks-edit -f -d $ROOT_DEV -s 1 -c "$NEW_TANG_PIN"
              echo "Pin applied successfully"
              touch /tmp/rekey-complete
              sleep infinity
            readinessProbe:
              exec:
                command:
                - cat
                - /host/tmp/rekey-complete
              initialDelaySeconds: 30
              periodSeconds: 10
            env:
            - name: ROOT_DEV
              value: /dev/disk/by-partlabel/root
            - name: NEW_TANG_PIN
              value: >-
                {"t":1,"pins":{"tang":[
                  {"url":"http://tangserver01:7500","thp":"WOjQYkyK7DxY_T5pMncMO5w0f6E"},
                  {"url":"http://tangserver02:7500","thp":"I5Ynh2JefoAO3tNH9TgI4obIaXI"},
                  {"url":"http://tangserver03:7500","thp":"38qWZVeDKzCPG9pHLqKzs6k1ons"}
                ]}}
            volumeMounts:
            - name: hostroot
              mountPath: /host
            securityContext:
              privileged: true
          volumes:
          - name: hostroot
            hostPath:
              path: /
          nodeSelector:
            kubernetes.io/os: linux
          priorityClassName: system-node-critical
          restartPolicy: Always
          serviceAccount: machine-config-daemon
          serviceAccountName: machine-config-daemon

    在这种情况下,即使您对 tangserver01 进行 rekeying,您不仅要为 tangserver01 指定新的指纹,还必须为所有其他 Tang 服务器指定当前的指纹。如果未指定重新密钥操作的所有指纹,则可能会存在中间人攻击机的风险。

  2. 要将守护进程集发布到必须重新密钥的每个集群,请运行以下命令:

    $ oc apply -f tang-rekey.yaml

    但是,为了大规模运行,请将守护进程集包装在 ACM 策略中。此 ACM 配置必须包含一个用于部署守护进程集的策略,第二个策略用于检查所有守护进程集 pod 是否都是 READY,以及一个放置规则,以将其应用到适当的集群集合。

注意

验证守护进程集是否已成功重新加密所有服务器后,请删除 守护进程集。如果您不删除守护进程集,则必须在下一次重新密钥操作之前删除它。

验证

分发守护进程集后,监控守护进程集,以确保成功完成重新密钥。示例守护进程集中的脚本在重新密钥失败时会终止并显示错误,如果成功,则保持 CURRENT 状态。还有一个就绪度探测,它会在重新密钥成功完成时将 pod 标记为 READY

  • 这是在重新密钥完成前守护进程集的输出列表示例:

    $ oc get -n openshift-machine-config-operator ds tang-rekey

    输出示例

    NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
    tang-rekey   1         1         0       1            0           kubernetes.io/os=linux   11s

  • 这是成功完成重新密钥后守护进程集的输出列表示例:

    $ oc get -n openshift-machine-config-operator ds tang-rekey

    输出示例

    NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
    tang-rekey   1         1         1       1            1           kubernetes.io/os=linux   13h

完成重新密钥通常需要几分钟时间。

注意

如果您使用 ACM 策略将守护进程集分发到多个集群,则必须包含一个检查每个守护进程集的 READY 计数等于 DESIRED 计数的合规性策略。这样,遵守此类策略就表明所有守护进程集 pod 都是 READY,并且重新密钥已成功完成。您还可以使用 ACM 搜索来查询所有守护进程集的状态。

16.3.3.3. Tang 服务器的临时重新密钥错误故障排除

要确定重新密钥 Tang 服务器的错误条件是否为临时,请执行以下步骤。临时错误条件可能包括:

  • 临时网络中断
  • Tang 服务器维护

通常,当发生这些类型的临时错误条件时,您可以等待守护进程集成功解决错误,或者您可以删除守护进程集,且不会重试直到临时错误条件解决为止。

流程

  1. 使用普通的 Kubernetes pod 重启策略,重启执行重新密钥操作的 pod。
  2. 如果有任何关联的 Tang 服务器不可用,请尝试重新密钥,直到所有服务器重新上线。

16.3.3.4. Tang 服务器的永久重新密钥错误故障排除

如果在重新密钥 Tang 服务器后,READY 计数在延长时间后不等于 DESIRED 计数,这可能表示永久故障条件。在这种情况下,可能适用以下条件:

  • Tang 服务器 URL 或 NEW_TANG_PIN 定义中的排字错误。
  • Tang 服务器已停用,或者密钥永久丢失。

先决条件

  • 此流程中显示的命令可以在 Tang 服务器或可通过网络访问 Tang 服务器的任何 Linux 系统上运行。

流程

  1. 对守护进程集中定义的每个 Tang 服务器配置执行简单的加密和解密操作,以验证 Tang 服务器配置。

    这是使用错误的指纹进行加密和解密尝试的示例:

    $ echo "okay" | clevis encrypt tang \
      '{"url":"http://tangserver02:7500","thp":"badthumbprint"}' | \
      clevis decrypt

    输出示例

    Unable to fetch advertisement: 'http://tangserver02:7500/adv/badthumbprint'!

    这是使用很好的指纹加密和解密尝试的示例:

    $ echo "okay" | clevis encrypt tang \
      '{"url":"http://tangserver03:7500","thp":"goodthumbprint"}' | \
      clevis decrypt

    输出示例

    okay

  2. 在确定根本原因后,纠正根本情况:

    1. 删除无法正常工作的守护进程集。
    2. 编辑守护进程集定义,以修复根本问题。这可能包括以下操作:

      • 编辑 Tang 服务器条目,以更正 URL 和 thumbprint。
      • 删除不再处于服务中的 Tang 服务器。
      • 添加新 Tang 服务器,该服务器替代已停用的服务器。
  3. 再次分发更新的守护进程集。
注意

当从配置替换、删除或添加 Tang 服务器时,只要至少一台原始服务器仍在正常工作,包括当前被重新密钥的服务器,就可以成功执行重新密钥操作。如果没有原始 Tang 服务器正常工作或可以恢复,则无法恢复系统,您必须重新部署受影响的节点。

验证

检查守护进程集中各个容器集的日志,以确定重新密钥是否成功完成。如果重新密钥不成功,日志可能会指示故障条件。

  1. 找到由守护进程集创建的容器的名称:

    $ oc get pods -A | grep tang-rekey

    输出示例

    openshift-machine-config-operator  tang-rekey-7ks6h  1/1  Running   20 (8m39s ago)  89m

  2. 显示容器中的日志。以下日志来自一个成功完成的重新密钥操作:

    $ oc logs tang-rekey-7ks6h

    输出示例

    Current tang pin:
    1: sss '{"t":1,"pins":{"tang":[{"url":"http://10.46.55.192:7500"},{"url":"http://10.46.55.192:7501"},{"url":"http://10.46.55.192:7502"}]}}'
    Applying new tang pin: {"t":1,"pins":{"tang":[
      {"url":"http://tangserver01:7500","thp":"WOjQYkyK7DxY_T5pMncMO5w0f6E"},
      {"url":"http://tangserver02:7500","thp":"I5Ynh2JefoAO3tNH9TgI4obIaXI"},
      {"url":"http://tangserver03:7500","thp":"38qWZVeDKzCPG9pHLqKzs6k1ons"}
    ]}}
    Updating binding...
    Binding edited successfully
    Pin applied successfully

16.3.4. 删除旧的 Tang 服务器密钥

先决条件

  • 运行 Tang 服务器的 Linux 计算机上的 root shell。

流程

  1. 找到并访问存储 Tang 服务器密钥的目录。这通常是 /var/db/tang 目录:

    # cd /var/db/tang/
  2. 列出当前的 Tang 服务器密钥,显示公告和未广播的密钥:

    # ls -A1

    输出示例

    .36AHjNH3NZDSnlONLz1-V4ie6t8.jwk
    .gJZiNPMLRBnyo_ZKfK4_5SrnHYo.jwk
    Bp8XjITceWSN_7XFfW7WfJDTomE.jwk
    WOjQYkyK7DxY_T5pMncMO5w0f6E.jwk

  3. 删除旧密钥:

    # rm .*.jwk
  4. 列出当前的 Tang 服务器密钥以验证未归档的密钥不再存在:

    # ls -A1

    输出示例

    Bp8XjITceWSN_7XFfW7WfJDTomE.jwk
    WOjQYkyK7DxY_T5pMncMO5w0f6E.jwk

验证

此时,服务器仍然会公告新密钥,但尝试根据旧密钥解密将失败。

  1. 查询 Tang 服务器以获取当前公告的键 thumbprints:

    # tang-show-keys 7500

    输出示例

    WOjQYkyK7DxY_T5pMncMO5w0f6E

  2. 解密之前创建的测试文件以验证旧密钥的解密失败:

    # clevis decrypt </tmp/encryptValidation

    输出示例

    Error communicating with the server!

如果您在共享相同关键资料的负载均衡器后面运行多个 Tang 服务器,请确保在继续之前,在此进行的更改会正确同步整个服务器集。