diff --git a/ocp-admin/scripts/security-validation/fetch_coreos_metadata.py b/ocp-admin/scripts/security-validation/fetch_coreos_metadata.py index 5af314f0..3db814a8 100644 --- a/ocp-admin/scripts/security-validation/fetch_coreos_metadata.py +++ b/ocp-admin/scripts/security-validation/fetch_coreos_metadata.py @@ -1,5 +1,9 @@ #!/usr/bin/env python3 -"""Fetch OCP release metadata and extract CoreOS RPM package list.""" +"""Fetch OCP release metadata and extract CoreOS RPM package list. + +Uses podman to read the release image's image-references manifest directly, +avoiding the need for the oc CLI (see MGMT-24450). +""" import argparse import json @@ -9,7 +13,8 @@ import sys OCP_VERSION_RE = re.compile(r"^4\.\d+\.\d+$") -TIMEOUT_RELEASE = 60 +RELEASE_IMAGE = "quay.io/openshift-release-dev/ocp-release" +TIMEOUT_RELEASE = 120 TIMEOUT_RPM = 300 @@ -23,7 +28,8 @@ def run_cmd(cmd, timeout=60): return -1, "", f"command not found: {cmd[0]}" -def parse_release_info(stdout): +def parse_image_references(image_refs_json): + """Extract release metadata from the image-references manifest.""" info = { "created": "", "machine_os": "", @@ -31,30 +37,27 @@ def parse_release_info(stdout): "coreos_pullspec": "", } - for line in stdout.split("\n"): - line = line.strip() - - if line.startswith("Created:"): - info["created"] = line.split("Created:", 1)[1].strip() + info["created"] = image_refs_json.get("metadata", {}).get("creationTimestamp", "") - if "machine-os" in line.lower() and "Red Hat Enterprise Linux CoreOS" in line: - parts = line.split() - for i, p in enumerate(parts): - if p == "machine-os" and i + 1 < len(parts): - info["machine_os"] = parts[i + 1] + for tag in image_refs_json.get("spec", {}).get("tags", []): + name = tag.get("name", "") + pullspec = tag.get("from", {}).get("name", "") + build_versions = tag.get("annotations", {}).get("io.openshift.build.versions", "") - for comp in ("kubernetes", "kubectl", "kubernetes-tests"): - if line.startswith(comp): - parts = line.split() - if len(parts) >= 2: - info["component_versions"][comp] = parts[1] + if name == "rhel-coreos": + info["coreos_pullspec"] = pullspec + for part in build_versions.split(","): + part = part.strip() + if part.startswith("machine-os="): + info["machine_os"] = part.split("=", 1)[1] - if line.startswith("rhel-coreos ") or line.startswith("rhel-coreos\t"): - parts = line.split() - for p in parts: - if p.startswith("quay.io/"): - info["coreos_pullspec"] = p - break + if build_versions: + for part in build_versions.split(","): + part = part.strip() + if "=" in part: + k, v = part.split("=", 1) + if k in ("kubernetes", "kubectl", "kubernetes-tests"): + info["component_versions"][k] = v return info @@ -151,43 +154,48 @@ def main(): errors = [] - if not shutil.which("oc"): + if not shutil.which("podman"): json.dump({ "ocp_version": args.ocp_version, - "error": "oc CLI not found in PATH. Install from https://console.redhat.com/openshift/downloads", - "errors": ["oc not installed"], + "error": "podman not found in PATH. Install podman before running.", + "errors": ["podman not installed"], }, sys.stdout, indent=2) print() sys.exit(1) - if not shutil.which("podman"): + release_image = f"{RELEASE_IMAGE}:{args.ocp_version}-x86_64" + image_refs_cmd = [ + "podman", "run", "--rm", "--entrypoint", "cat", + release_image, "/release-manifests/image-references", + ] + + rc, stdout, stderr = run_cmd(image_refs_cmd, timeout=TIMEOUT_RELEASE) + if rc != 0: json.dump({ "ocp_version": args.ocp_version, - "error": "podman not found in PATH. Install podman before running.", - "errors": ["podman not installed"], + "error": f"Failed to read image-references from release image: {stderr.strip()}", + "errors": [f"podman failed (exit {rc}): {stderr.strip()[:300]}"], }, sys.stdout, indent=2) print() sys.exit(1) - rc, stdout, stderr = run_cmd( - ["oc", "adm", "release", "info", args.ocp_version, "--pullspecs"], - timeout=TIMEOUT_RELEASE - ) - if rc != 0: + try: + image_refs_json = json.loads(stdout) + except json.JSONDecodeError as e: json.dump({ "ocp_version": args.ocp_version, - "error": f"oc adm release info failed: {stderr.strip()}", - "errors": [f"oc failed (exit {rc}): {stderr.strip()[:300]}"], + "error": f"Invalid image-references JSON: {e}", + "errors": ["Failed to parse image-references"], }, sys.stdout, indent=2) print() sys.exit(1) - info = parse_release_info(stdout) + info = parse_image_references(image_refs_json) if not info["coreos_pullspec"]: json.dump({ "ocp_version": args.ocp_version, - "error": "Could not find rhel-coreos pullspec in release info", + "error": "Could not find rhel-coreos pullspec in image-references", "errors": ["rhel-coreos image not found in release"], }, sys.stdout, indent=2) print() diff --git a/ocp-admin/skills/cluster-creator/SKILL.md b/ocp-admin/skills/cluster-creator/SKILL.md index cfc0a3a9..cd923dc8 100644 --- a/ocp-admin/skills/cluster-creator/SKILL.md +++ b/ocp-admin/skills/cluster-creator/SKILL.md @@ -89,7 +89,7 @@ This skill uses `openshift-self-managed` MCP server exclusively. This server con **Do NOT use when**: - Listing/inspecting clusters → Use `/cluster-inventory` skill -- Managing workloads → Out of scope (use `oc` CLI with credentials from this skill) +- Managing workloads → Out of scope (use MCP tools or CLI with credentials from this skill) - Troubleshooting → Use `/cluster-inventory` skill - Upgrading clusters (not supported) @@ -420,7 +420,7 @@ Console: https://console-openshift-console.apps.{cluster_name}.{base_domain} Next Steps: - Verify via MCP: export KUBECONFIG=/tmp/{cluster_name}.{base_domain}/kubeconfig MCP Tool: resources_list (Parameters: {kind: "Node"}) -- Alternative (oc CLI, unlikely available): oc --kubeconfig get nodes +- Alternative (CLI, unlikely available): oc --kubeconfig get nodes - SSH to nodes (if key configured): ssh -i /tmp/{cluster_name}.{base_domain}/ssh-key core@ - Web console: kubeadmin + password from /tmp/{cluster_name}.{base_domain}/kubeadmin-password - Configure identity provider (idp.md), RBAC (rbac.md) diff --git a/ocp-admin/skills/coreos-cve-validator/SKILL.md b/ocp-admin/skills/coreos-cve-validator/SKILL.md index 101a2e0a..31c40b65 100644 --- a/ocp-admin/skills/coreos-cve-validator/SKILL.md +++ b/ocp-admin/skills/coreos-cve-validator/SKILL.md @@ -25,7 +25,7 @@ Two required inputs: SCRIPTS_DIR="scripts" test -f "$SCRIPTS_DIR/validate_input.py" || { echo "Error: Scripts directory not found at $SCRIPTS_DIR"; exit 1; } ``` -The scripts check internally for `oc` and `podman`. +The scripts check internally for `podman`. ## Workflow @@ -99,7 +99,7 @@ python $SCRIPTS_DIR/fetch_coreos_metadata.py [OCP_VERSION] --cache-dir /tmp/core **Caching:** The `--cache-dir` flag stores the result per OCP version. When validating multiple CVEs against the same OCP version, the CoreOS RPM list is fetched once and reused from cache for all subsequent CVEs. Always pass the same cache dir for all scans in a session. -This fetches OCP release info via `oc adm release info` (read-only query against public Red Hat release metadata — does not access the user's cluster) and runs `podman` to extract the RPM package list from the CoreOS image. It returns: +This reads the release image's `image-references` manifest via `podman` (read-only query against public Red Hat release metadata at quay.io — does not access the user's cluster) and then extracts the RPM package list from the CoreOS image. It returns: - `ocp_version`, `created`, `machine_os` (RHEL version), `rhel_version`, `rhel_major` - `coreos_pullspec` — the exact CoreOS image digest - `cpes` — `rhel` and `ocp` CPE strings for VEX matching