From 6d2ce439f2ac88d2872d96969e6d31369782f4f9 Mon Sep 17 00:00:00 2001 From: Chris Butler Date: Wed, 19 Aug 2026 06:38:37 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20add=20check-dcap-expiry.sh=20?= =?UTF-8?q?=E2=80=94=20verify=20TDX=20collateral=20and=20PCK=20cert=20expi?= =?UTF-8?q?ry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checks three sources on the live cluster: 1. platform_collaterals.json in trustee-operator-system (TCB info, QE identity issueDate/nextUpdate) 2. PCK cache secrets in intel-dcap-operator-system (embedded TCB expiry from binary pcsclient.py cache output) 3. Platform data secrets (QE ID presence) Reports OK/EXPIRING (<7d)/EXPIRED with days remaining. Exit 1 on issues. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/check-dcap-expiry.sh | 165 +++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100755 scripts/check-dcap-expiry.sh diff --git a/scripts/check-dcap-expiry.sh b/scripts/check-dcap-expiry.sh new file mode 100755 index 00000000..0c08fac7 --- /dev/null +++ b/scripts/check-dcap-expiry.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# Check expiry status of TDX DCAP collateral and PCK certificates deployed on the cluster. +# +# Checks: +# 1. platform_collaterals.json in trustee-operator-system (TCB info, QE identity expiry) +# 2. PCK cache secrets in intel-dcap-operator-system (embedded TCB info expiry) +# +# Usage: +# ./scripts/check-dcap-expiry.sh +# +# Requires: oc (logged in), python3, base64 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +COLLATERAL_NS="trustee-operator-system" +COLLATERAL_SECRET="tdx-collateral" +PCK_NS="intel-dcap-operator-system" + +fail=0 + +check_date() { + local label="$1" next_update="$2" + local expiry_epoch now_epoch days_left + expiry_epoch=$(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('${next_update}'.replace('Z','+00:00')).timestamp()))") + now_epoch=$(date +%s) + days_left=$(( (expiry_epoch - now_epoch) / 86400 )) + + if [ "$days_left" -lt 0 ]; then + echo -e " ${RED}EXPIRED${NC} ${label}: nextUpdate=${next_update} (${days_left}d ago)" + fail=1 + elif [ "$days_left" -lt 7 ]; then + echo -e " ${YELLOW}EXPIRING${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + else + echo -e " ${GREEN}OK${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + fi +} + +# ── 1. DCAP Collateral (platform_collaterals.json) ────────────────────────── + +echo "=== DCAP Collateral (${COLLATERAL_NS}/${COLLATERAL_SECRET}) ===" + +if ! oc get secret "$COLLATERAL_SECRET" -n "$COLLATERAL_NS" &>/dev/null; then + echo -e " ${RED}MISSING${NC} Secret ${COLLATERAL_SECRET} not found in ${COLLATERAL_NS}" + fail=1 +else + COLLATERAL_JSON=$(oc get secret "$COLLATERAL_SECRET" -n "$COLLATERAL_NS" \ + -o jsonpath='{.data.platform_collaterals\.json}' | base64 -d) + + python3 -c " +import json, sys + +data = json.loads('''${COLLATERAL_JSON}'''.replace(\"'''\", '')) +" 2>/dev/null || { + # Fallback: pipe through stdin for large JSON + true + } + + # Extract TCB info expiry dates + echo "$COLLATERAL_JSON" | python3 -c " +import json, sys +data = json.load(sys.stdin) +col = data.get('collaterals', {}) +results = [] + +# TCB info entries +for ti in col.get('tcbinfos', []): + fmspc = ti.get('fmspc', 'unknown') + for key in ['sgx_tcbinfo_early', 'sgx_tcbinfo', 'tdx_tcbinfo_early', 'tdx_tcbinfo']: + info = ti.get(key, {}) + if isinstance(info, dict): + tcb = info.get('tcbInfo', {}) + else: + continue + nu = tcb.get('nextUpdate') + if nu: + results.append((f'{key} FMSPC={fmspc}', nu)) + +# QE identity entries +for qi in col.get('qeidentities', []): + for key in ['qe_identity_early', 'qe_identity']: + ei = qi.get(key, {}) + if isinstance(ei, str): + try: + ei = json.loads(ei) + except json.JSONDecodeError: + continue + if isinstance(ei, dict): + info = ei.get('enclaveIdentity', {}) + nu = info.get('nextUpdate') + if nu: + results.append((f'{key}', nu)) + +for label, nu in results: + print(f'{label}|{nu}') +" | while IFS='|' read -r label next_update; do + check_date "$label" "$next_update" + done + + echo "" +fi + +# ── 2. PCK Certificates (intel-dcap-operator-system) ───────────────────────── + +echo "=== PCK Cache Secrets (${PCK_NS}) ===" + +PCK_SECRETS=$(oc get secrets -n "$PCK_NS" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep -E '^[0-9a-f]{32}-pck$' || true) + +if [ -z "$PCK_SECRETS" ]; then + echo -e " ${RED}MISSING${NC} No PCK cert secrets found (expected -pck)" + fail=1 +else + for secret_name in $PCK_SECRETS; do + qe_id="${secret_name%-pck}" + echo " PCK secret: ${secret_name} (QE ID: ${qe_id})" + + # The PCK cache secret is a binary blob with embedded JSON TCB info + oc get secret "$secret_name" -n "$PCK_NS" -o jsonpath='{.data.certificate}' | \ + base64 -d | python3 -c " +import sys, re +data = sys.stdin.buffer.read() +text = data.decode('ascii', errors='ignore') +matches = re.findall(r'\"nextUpdate\":\"([^\"]+)\"', text) +if matches: + for m in matches: + print(m) +else: + print('NONE') +" | while read -r next_update; do + if [ "$next_update" = "NONE" ]; then + echo -e " ${YELLOW}UNKNOWN${NC} No expiry date found in PCK cache blob" + else + check_date " embedded TCB" "$next_update" + fi + done + done +fi + +# ── 3. Platform Data Secret ────────────────────────────────────────────────── + +echo "" +echo "=== Platform Data (${PCK_NS}) ===" + +PLATFORM_SECRETS=$(oc get secrets -n "$PCK_NS" -l type=platform-data --no-headers -o custom-columns=NAME:.metadata.name 2>/dev/null || true) + +if [ -z "$PLATFORM_SECRETS" ]; then + echo -e " ${RED}MISSING${NC} No platform-data secrets found" + fail=1 +else + for ps in $PLATFORM_SECRETS; do + echo -e " ${GREEN}OK${NC} QE ID: ${ps}" + done +fi + +echo "" +if [ "$fail" -ne 0 ]; then + echo -e "${RED}RESULT: ISSUES FOUND${NC} — see above" + exit 1 +else + echo -e "${GREEN}RESULT: ALL OK${NC}" +fi From 6e13f99065735cd3fbb139f2b3524ae73a432a10 Mon Sep 17 00:00:00 2001 From: Chris Butler Date: Wed, 19 Aug 2026 07:04:24 +0000 Subject: [PATCH 2/2] refactor: split check-dcap-expiry into collateral and PCK scripts - check-collateral-expiry.sh: trustee-operator-system collateral (TCB info, QE identity nextUpdate from platform_collaterals.json) - check-pck-expiry.sh: intel-dcap-operator-system PCK cache (embedded TCB expiry from binary blob) + platform-data QE ID Both accept namespace as optional $1 argument. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/check-collateral-expiry.sh | 89 ++++++++++++++++ scripts/check-dcap-expiry.sh | 165 ----------------------------- scripts/check-pck-expiry.sh | 99 +++++++++++++++++ 3 files changed, 188 insertions(+), 165 deletions(-) create mode 100755 scripts/check-collateral-expiry.sh delete mode 100755 scripts/check-dcap-expiry.sh create mode 100755 scripts/check-pck-expiry.sh diff --git a/scripts/check-collateral-expiry.sh b/scripts/check-collateral-expiry.sh new file mode 100755 index 00000000..ca057519 --- /dev/null +++ b/scripts/check-collateral-expiry.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Check expiry of TDX DCAP collateral deployed in trustee-operator-system. +# +# Parses platform_collaterals.json from the tdx-collateral Secret for +# TCB info and QE identity nextUpdate dates. +# +# Usage: +# ./scripts/check-collateral-expiry.sh +# +# Requires: oc (logged in), python3, base64 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +NS="${1:-trustee-operator-system}" +SECRET="${2:-tdx-collateral}" + +fail=0 + +check_date() { + local label="$1" next_update="$2" + local expiry_epoch now_epoch days_left + expiry_epoch=$(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('${next_update}'.replace('Z','+00:00')).timestamp()))") + now_epoch=$(date +%s) + days_left=$(( (expiry_epoch - now_epoch) / 86400 )) + + if [ "$days_left" -lt 0 ]; then + echo -e " ${RED}EXPIRED${NC} ${label}: nextUpdate=${next_update} (${days_left}d ago)" + fail=1 + elif [ "$days_left" -lt 7 ]; then + echo -e " ${YELLOW}EXPIRING${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + else + echo -e " ${GREEN}OK${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + fi +} + +echo "=== DCAP Collateral (${NS}/${SECRET}) ===" + +if ! oc get secret "$SECRET" -n "$NS" &>/dev/null; then + echo -e " ${RED}MISSING${NC} Secret ${SECRET} not found in ${NS}" + exit 1 +fi + +oc get secret "$SECRET" -n "$NS" \ + -o jsonpath='{.data.platform_collaterals\.json}' | base64 -d | python3 -c " +import json, sys +data = json.load(sys.stdin) +col = data.get('collaterals', {}) + +for ti in col.get('tcbinfos', []): + fmspc = ti.get('fmspc', 'unknown') + for key in ['sgx_tcbinfo_early', 'sgx_tcbinfo', 'tdx_tcbinfo_early', 'tdx_tcbinfo']: + info = ti.get(key, {}) + if isinstance(info, dict): + tcb = info.get('tcbInfo', {}) + else: + continue + nu = tcb.get('nextUpdate') + if nu: + print(f'{key} FMSPC={fmspc}|{nu}') + +for qi in col.get('qeidentities', []): + for key in ['qe_identity_early', 'qe_identity']: + ei = qi.get(key, {}) + if isinstance(ei, str): + try: + ei = json.loads(ei) + except json.JSONDecodeError: + continue + if isinstance(ei, dict): + info = ei.get('enclaveIdentity', {}) + nu = info.get('nextUpdate') + if nu: + print(f'{key}|{nu}') +" | while IFS='|' read -r label next_update; do + check_date "$label" "$next_update" +done + +echo "" +if [ "$fail" -ne 0 ]; then + echo -e "${RED}RESULT: ISSUES FOUND${NC}" + exit 1 +else + echo -e "${GREEN}RESULT: ALL OK${NC}" +fi diff --git a/scripts/check-dcap-expiry.sh b/scripts/check-dcap-expiry.sh deleted file mode 100755 index 0c08fac7..00000000 --- a/scripts/check-dcap-expiry.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env bash -# Check expiry status of TDX DCAP collateral and PCK certificates deployed on the cluster. -# -# Checks: -# 1. platform_collaterals.json in trustee-operator-system (TCB info, QE identity expiry) -# 2. PCK cache secrets in intel-dcap-operator-system (embedded TCB info expiry) -# -# Usage: -# ./scripts/check-dcap-expiry.sh -# -# Requires: oc (logged in), python3, base64 - -set -euo pipefail - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -COLLATERAL_NS="trustee-operator-system" -COLLATERAL_SECRET="tdx-collateral" -PCK_NS="intel-dcap-operator-system" - -fail=0 - -check_date() { - local label="$1" next_update="$2" - local expiry_epoch now_epoch days_left - expiry_epoch=$(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('${next_update}'.replace('Z','+00:00')).timestamp()))") - now_epoch=$(date +%s) - days_left=$(( (expiry_epoch - now_epoch) / 86400 )) - - if [ "$days_left" -lt 0 ]; then - echo -e " ${RED}EXPIRED${NC} ${label}: nextUpdate=${next_update} (${days_left}d ago)" - fail=1 - elif [ "$days_left" -lt 7 ]; then - echo -e " ${YELLOW}EXPIRING${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" - else - echo -e " ${GREEN}OK${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" - fi -} - -# ── 1. DCAP Collateral (platform_collaterals.json) ────────────────────────── - -echo "=== DCAP Collateral (${COLLATERAL_NS}/${COLLATERAL_SECRET}) ===" - -if ! oc get secret "$COLLATERAL_SECRET" -n "$COLLATERAL_NS" &>/dev/null; then - echo -e " ${RED}MISSING${NC} Secret ${COLLATERAL_SECRET} not found in ${COLLATERAL_NS}" - fail=1 -else - COLLATERAL_JSON=$(oc get secret "$COLLATERAL_SECRET" -n "$COLLATERAL_NS" \ - -o jsonpath='{.data.platform_collaterals\.json}' | base64 -d) - - python3 -c " -import json, sys - -data = json.loads('''${COLLATERAL_JSON}'''.replace(\"'''\", '')) -" 2>/dev/null || { - # Fallback: pipe through stdin for large JSON - true - } - - # Extract TCB info expiry dates - echo "$COLLATERAL_JSON" | python3 -c " -import json, sys -data = json.load(sys.stdin) -col = data.get('collaterals', {}) -results = [] - -# TCB info entries -for ti in col.get('tcbinfos', []): - fmspc = ti.get('fmspc', 'unknown') - for key in ['sgx_tcbinfo_early', 'sgx_tcbinfo', 'tdx_tcbinfo_early', 'tdx_tcbinfo']: - info = ti.get(key, {}) - if isinstance(info, dict): - tcb = info.get('tcbInfo', {}) - else: - continue - nu = tcb.get('nextUpdate') - if nu: - results.append((f'{key} FMSPC={fmspc}', nu)) - -# QE identity entries -for qi in col.get('qeidentities', []): - for key in ['qe_identity_early', 'qe_identity']: - ei = qi.get(key, {}) - if isinstance(ei, str): - try: - ei = json.loads(ei) - except json.JSONDecodeError: - continue - if isinstance(ei, dict): - info = ei.get('enclaveIdentity', {}) - nu = info.get('nextUpdate') - if nu: - results.append((f'{key}', nu)) - -for label, nu in results: - print(f'{label}|{nu}') -" | while IFS='|' read -r label next_update; do - check_date "$label" "$next_update" - done - - echo "" -fi - -# ── 2. PCK Certificates (intel-dcap-operator-system) ───────────────────────── - -echo "=== PCK Cache Secrets (${PCK_NS}) ===" - -PCK_SECRETS=$(oc get secrets -n "$PCK_NS" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep -E '^[0-9a-f]{32}-pck$' || true) - -if [ -z "$PCK_SECRETS" ]; then - echo -e " ${RED}MISSING${NC} No PCK cert secrets found (expected -pck)" - fail=1 -else - for secret_name in $PCK_SECRETS; do - qe_id="${secret_name%-pck}" - echo " PCK secret: ${secret_name} (QE ID: ${qe_id})" - - # The PCK cache secret is a binary blob with embedded JSON TCB info - oc get secret "$secret_name" -n "$PCK_NS" -o jsonpath='{.data.certificate}' | \ - base64 -d | python3 -c " -import sys, re -data = sys.stdin.buffer.read() -text = data.decode('ascii', errors='ignore') -matches = re.findall(r'\"nextUpdate\":\"([^\"]+)\"', text) -if matches: - for m in matches: - print(m) -else: - print('NONE') -" | while read -r next_update; do - if [ "$next_update" = "NONE" ]; then - echo -e " ${YELLOW}UNKNOWN${NC} No expiry date found in PCK cache blob" - else - check_date " embedded TCB" "$next_update" - fi - done - done -fi - -# ── 3. Platform Data Secret ────────────────────────────────────────────────── - -echo "" -echo "=== Platform Data (${PCK_NS}) ===" - -PLATFORM_SECRETS=$(oc get secrets -n "$PCK_NS" -l type=platform-data --no-headers -o custom-columns=NAME:.metadata.name 2>/dev/null || true) - -if [ -z "$PLATFORM_SECRETS" ]; then - echo -e " ${RED}MISSING${NC} No platform-data secrets found" - fail=1 -else - for ps in $PLATFORM_SECRETS; do - echo -e " ${GREEN}OK${NC} QE ID: ${ps}" - done -fi - -echo "" -if [ "$fail" -ne 0 ]; then - echo -e "${RED}RESULT: ISSUES FOUND${NC} — see above" - exit 1 -else - echo -e "${GREEN}RESULT: ALL OK${NC}" -fi diff --git a/scripts/check-pck-expiry.sh b/scripts/check-pck-expiry.sh new file mode 100755 index 00000000..60df6845 --- /dev/null +++ b/scripts/check-pck-expiry.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Check expiry of PCK cache secrets and platform data in intel-dcap-operator-system. +# +# PCK cache secrets contain a binary blob from pcsclient.py with embedded +# JSON TCB info including nextUpdate dates. This script extracts and checks them. +# +# Usage: +# ./scripts/check-pck-expiry.sh +# +# Requires: oc (logged in), python3, base64 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +NS="${1:-intel-dcap-operator-system}" + +fail=0 + +check_date() { + local label="$1" next_update="$2" + local expiry_epoch now_epoch days_left + expiry_epoch=$(python3 -c "from datetime import datetime; print(int(datetime.fromisoformat('${next_update}'.replace('Z','+00:00')).timestamp()))") + now_epoch=$(date +%s) + days_left=$(( (expiry_epoch - now_epoch) / 86400 )) + + if [ "$days_left" -lt 0 ]; then + echo -e " ${RED}EXPIRED${NC} ${label}: nextUpdate=${next_update} (${days_left}d ago)" + fail=1 + elif [ "$days_left" -lt 7 ]; then + echo -e " ${YELLOW}EXPIRING${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + else + echo -e " ${GREEN}OK${NC} ${label}: nextUpdate=${next_update} (${days_left}d left)" + fi +} + +# ── PCK Cache Secrets ──────────────────────────────────────────────────────── + +echo "=== PCK Cache Secrets (${NS}) ===" + +PCK_SECRETS=$(oc get secrets -n "$NS" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep -E '^[0-9a-f]{32}-pck$' || true) + +if [ -z "$PCK_SECRETS" ]; then + echo -e " ${RED}MISSING${NC} No PCK cert secrets found (expected -pck)" + fail=1 +else + for secret_name in $PCK_SECRETS; do + qe_id="${secret_name%-pck}" + echo " PCK secret: ${secret_name} (QE ID: ${qe_id})" + + DATES=$(oc get secret "$secret_name" -n "$NS" -o jsonpath='{.data.certificate}' | \ + base64 -d | python3 -c " +import sys, re +data = sys.stdin.buffer.read() +text = data.decode('ascii', errors='ignore') +matches = re.findall(r'\"nextUpdate\":\"([^\"]+)\"', text) +if matches: + for m in matches: + print(m) +else: + print('NONE') +") + + if [ "$DATES" = "NONE" ]; then + echo -e " ${YELLOW}UNKNOWN${NC} No expiry date found in PCK cache blob" + else + echo "$DATES" | while read -r next_update; do + check_date " embedded TCB" "$next_update" + done + fi + done +fi + +# ── Platform Data ──────────────────────────────────────────────────────────── + +echo "" +echo "=== Platform Data (${NS}) ===" + +PLATFORM_SECRETS=$(oc get secrets -n "$NS" -l type=platform-data --no-headers -o custom-columns=NAME:.metadata.name 2>/dev/null || true) + +if [ -z "$PLATFORM_SECRETS" ]; then + echo -e " ${RED}MISSING${NC} No platform-data secrets found" + fail=1 +else + for ps in $PLATFORM_SECRETS; do + echo -e " ${GREEN}OK${NC} QE ID: ${ps}" + done +fi + +echo "" +if [ "$fail" -ne 0 ]; then + echo -e "${RED}RESULT: ISSUES FOUND${NC}" + exit 1 +else + echo -e "${GREEN}RESULT: ALL OK${NC}" +fi