Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 47 additions & 39 deletions ocp-admin/scripts/security-validation/fetch_coreos_metadata.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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


Expand All @@ -23,38 +28,36 @@ 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": "",
"component_versions": {},
"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

Expand Down Expand Up @@ -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"
Comment thread
r2dedios marked this conversation as resolved.
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()
Expand Down
4 changes: 2 additions & 2 deletions ocp-admin/skills/cluster-creator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 <path> get nodes
- Alternative (CLI, unlikely available): oc --kubeconfig <path> get nodes
Comment thread
r2dedios marked this conversation as resolved.
- SSH to nodes (if key configured): ssh -i /tmp/{cluster_name}.{base_domain}/ssh-key core@<node-ip>
- Web console: kubeadmin + password from /tmp/{cluster_name}.{base_domain}/kubeadmin-password
- Configure identity provider (idp.md), RBAC (rbac.md)
Expand Down
4 changes: 2 additions & 2 deletions ocp-admin/skills/coreos-cve-validator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
Loading