feat(tls): reload and publish external certificate generations (#8467)

* feat(tls): reload external certificate snapshots coherently

Pin projected certificate generations, validate complete TLS material, coordinate server consumers, and invalidate outbound RPC caches coherently. Add externally managed TLS Secret support and rotation guidance.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* feat(tls): publish server certificates through a fenced admin API

Add opt-in server-leaf publication with dedicated IAM actions, actual TLS
transport checks, immutable generations, durable captured-token CAS and
idempotent receipts. Preserve independent trust and client identity sources
and distinguish visible publication state from the full serving chain.

Validate 64 TLS tests, 41 TLS-runtime tests, the focused IAM policy test,
and eight live local API scenarios including least privilege and restart.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* fix(admin): route certificate handler types through S3 facade

Use the existing admin S3 facade to keep direct s3s imports within the footprint ratchet.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* test(admin): include TLS certificate routes in route matrix

Account for the registered certificate-state GET and certificate publication POST without changing route behavior or weakening matrix assertions.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* fix(tls): reconcile startup trust before skipping reloads

Compare the initialized listener bundle with coherently published outbound material, retain startup trust and client identity when sources disappear, and cover independent startup rotations through real reload passes.

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* refactor(tls): reuse pinned generations and prepared listener state

Co-Authored-By: heihutu <heihutu@gmail.com>
Co-Authored-By: zhi22915 <qiuzgang@gmail.com>

* fix(tls): preserve startup trust source requirements

---------

Co-authored-by: heihutu <heihutu@gmail.com>
Co-authored-by: zhi22915 <qiuzgang@gmail.com>
Co-authored-by: overtrue <anzhengchao@gmail.com>
This commit is contained in:
Hauser
2026-10-12 08:53:11 +08:00
committed by GitHub
parent acd26afd8b
commit 1266d4c270
36 changed files with 4594 additions and 609 deletions
+3 -4
View File
@@ -568,7 +568,7 @@ nondefault_statefulset=$(awk '
nondefault_local_host=$(statefulset_env_value RUSTFS_LOCAL_ENDPOINT_HOST <<<"$nondefault_statefulset")
nondefault_namespace=$(yq eval '.metadata.namespace' - <<<"$nondefault_statefulset")
nondefault_endpoint_port=$(yq eval '.spec.template.spec.containers[0].ports[] | select(.name == "endpoint") | .containerPort' - <<<"$nondefault_statefulset")
mtls_liveness_command=$(yq eval '.spec.template.spec.containers[0].livenessProbe.exec.command[-1]' - <<<"$nondefault_statefulset")
mtls_liveness_port=$(yq eval '.spec.template.spec.containers[0].livenessProbe.tcpSocket.port' - <<<"$nondefault_statefulset")
mtls_readiness_command=$(yq eval '.spec.template.spec.containers[0].readinessProbe.exec.command[-1]' - <<<"$nondefault_statefulset")
if [[ "$nondefault_local_host" != '$(RUSTFS_CHART_POD_NAME).tenant-rustfs-headless.object-storage.svc.cluster.corp' ]]; then
echo "Combined generated topology produced an unexpected local endpoint host: $nondefault_local_host" >&2
@@ -578,9 +578,8 @@ if [[ "$nondefault_namespace" != "object-storage" || "$nondefault_endpoint_port"
echo "Combined generated topology must retain its namespace and custom endpoint port" >&2
exit 1
fi
if ! grep -Fq 'https://127.0.0.1:9443/health' <<<"$mtls_liveness_command" ||
grep -Fq '/health/ready' <<<"$mtls_liveness_command"; then
echo "mTLS liveness must use the process health endpoint, not readiness" >&2
if [[ "$mtls_liveness_port" != 9443 ]]; then
echo "mTLS liveness must check the listener independently of certificate validity" >&2
exit 1
fi
if ! grep -Fq 'https://127.0.0.1:9443/health/ready' <<<"$mtls_readiness_command"; then
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
CHART_DIR="$ROOT_DIR/helm/rustfs"
for distributed in false true; do
rendered=$(helm template tls-reload "$CHART_DIR" \
--set secret.allowInsecureDefaults=true \
--set mode.distributed.enabled="$distributed" \
--set mode.standalone.enabled="$([[ "$distributed" == false ]] && echo true || echo false)" \
--set tls.enabled=true --set tls.existingSecret=issued-tls)
workload=$(yq eval 'select(.kind == "Deployment" or .kind == "StatefulSet")' - <<<"$rendered")
mount=$(yq eval '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "tls-material")' - <<<"$workload")
[[ $(yq eval '.mountPath' - <<<"$mount") == /opt/tls ]]
[[ $(yq eval '.readOnly' - <<<"$mount") == true ]]
[[ $(yq eval 'has("subPath")' - <<<"$mount") == false ]]
keys=$(yq eval '.spec.template.spec.volumes[] | select(.name == "tls-material") | .projected.sources[].secret.items[].key' - <<<"$workload")
[[ "$keys" == $'tls.crt\ntls.key' ]]
[[ $(yq eval '.spec.template.spec.containers[0].readinessProbe.httpGet.scheme' - <<<"$workload") == HTTPS ]]
[[ $(yq eval '.spec.template.spec.containers[0].livenessProbe.tcpSocket.port' - <<<"$workload") == 9000 ]]
config=$(yq eval 'select(.kind == "ConfigMap") | .data' - <<<"$rendered")
[[ $(yq eval '.RUSTFS_TLS_RELOAD_ENABLE' - <<<"$config") == 1 ]]
[[ $(yq eval '.RUSTFS_TLS_KEYLOG' - <<<"$config") == 0 ]]
if [[ "$distributed" == true ]]; then
[[ $(yq eval '.RUSTFS_VOLUMES' - <<<"$config") == https://* ]]
fi
mtls=$(helm template tls-reload "$CHART_DIR" --set secret.allowInsecureDefaults=true \
--set mode.distributed.enabled="$distributed" \
--set mode.standalone.enabled="$([[ "$distributed" == false ]] && echo true || echo false)" \
--set mtls.enabled=true)
workload=$(yq eval 'select(.kind == "Deployment" or .kind == "StatefulSet")' - <<<"$mtls")
[[ $(yq eval '.spec.template.spec.volumes[] | select(.name == "tls-material") | .projected.sources | length' - <<<"$workload") == 2 ]]
[[ $(yq eval '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "tls-material") | has("subPath")' - <<<"$workload") == false ]]
[[ $(yq eval '.spec.template.spec.containers[0].livenessProbe.tcpSocket.port' - <<<"$workload") == 9000 ]]
done
if helm template tls-reload "$CHART_DIR" --set secret.allowInsecureDefaults=true \
--set tls.enabled=true --set tls.existingSecret=issued-tls --set mtls.enabled=true >/dev/null 2>&1; then
echo "Public TLS must not silently enable global client authentication" >&2
exit 1
fi
if helm template tls-reload "$CHART_DIR" --set secret.allowInsecureDefaults=true \
--set tls.enabled=true >/dev/null 2>&1; then
echo "Native TLS requires an explicitly configured serving Secret" >&2
exit 1
fi
echo "TLS Secret projection, rotation settings, topology and probes passed"