Security operations teams face a persistent gap: vulnerability scanners surface CVEs against SBOMs, but connecting that discovery to actual OS and application remediation still requires manual coordination across multiple teams and tools. This guide demonstrates a closed-loop, event-driven pipeline that detects a CVE disclosure, correlates it against infrastructure SBOMs stored in Red Hat Trusted Profile Analyzer (RHTPA), and automatically patches both OS packages and application container dependencies – without manual handoffs.
Traditional vulnerability management treats patching as a human process: a scanner detects a CVE, a ticket is raised, an engineer manually identifies affected packages, applies a fix, and closes the ticket. At scale this falls apart. A critical CVE affecting hundreds of hosts and multiple containerised applications can take days to fully remediate – days during which the exposure remains active.
Red Hat Trusted Profile Analyzer (RHTPA) changes the picture by maintaining a continuously updated inventory of Software Bill of Materials (SBOMs) for every workload in the environment. When a CVE is disclosed, RHTPA can immediately answer: which hosts run the affected package? Which container images include the vulnerable dependency?
Lightwell provides the security intelligence feed and the application-layer context needed to drive remediation for containerised workloads – triggering CI/CD pipelines to rebuild container images when a dependency is patched upstream. Combined with Event-Driven Ansible (EDA) as the integration layer, the entire detection-to-remediation cycle becomes a single automated pipeline.
What makes up this solution?
dnf and updated container images deployed via Podman| Persona | Challenge | What They Gain |
|---|---|---|
| Security Operations Engineer | Manually triaging CVEs, SSH-ing into hosts to check package versions, coordinating with DevOps for container rebuilds | Single-pipeline automation: CVE in → patched host and container out, with an AAP job history as audit trail |
| Platform / DevOps Lead | Container image vulnerabilities discovered post-deployment, manual rebuild coordination | Automatic container rebuild triggered by the same pipeline, with CI/CD and GitOps paths both supported |
| CISO / Security Director | Unable to show mean-time-to-remediation (MTTR) for critical CVEs; no quantified coverage across SBOMs | Measurable pipeline execution times per CVE, RHTPA SBOM coverage as the baseline, and AAP job completion as remediation evidence |
| Collection | Type | Purpose |
|---|---|---|
| ansible.eda | Certified | EDA webhook sources and rulebook execution |
| ansible.controller | Certified | Controller configuration-as-code: job templates, workflows, credentials |
| redhat.rhel_system_roles | Certified | OS-level configuration management on RHEL |
| community.general | Community | General-purpose modules including package management helpers |
| ansible.posix | Certified | POSIX system management |
| containers.podman | Certified | Podman container lifecycle management |
| System | Required | Purpose |
|---|---|---|
| RHTPA | Yes | SBOM storage and CVE correlation |
| Keycloak | Yes | OIDC authentication for RHTPA and AAP |
| Gitea | Yes | Application source hosting and CI pipeline trigger |
| Lightwell | Yes | CVE disclosure events and dependency fix notifications |
Operational Impact: High – automated patching reboots or restarts services; container rebuilds affect running workloads.
Build a custom Execution Environment containing syft, grype, and the collections listed above:
# execution-environment/execution-environment.yml
version: 3
images:
base_image:
name: registry.redhat.io/ansible-automation-platform/ee-minimal-rhel9:latest
dependencies:
python_interpreter:
package_system: python3.11
python:
- jmespath
system:
- curl
- jq
- syft
- grype
galaxy:
collections:
- name: ansible.eda
- name: ansible.controller
- name: redhat.rhel_system_roles
- name: community.general
- name: ansible.posix
- name: containers.podman
Tip: Build with
ansible-builder.Run
ansible-builder build -t lightwell-ee:latest -f execution-environment/execution-environment.ymlto produce the EE image. Push it to your private automation hub or registry before configuring the job templates.
The pipeline is triggered entirely by events – no polling, no scheduled scans. When Lightwell detects a CVE or a vendor fix becomes available, it sends a webhook payload to the EDA Controller, which evaluates the event against the cve_lifecycle.yml rulebook and launches the appropriate AAP workflow.
graph TD
A([CVE Event / Webhook]) -->|Payload| B(AAP EDA Controller)
B -->|Trigger| C(AAP Controller)
C -->|Query SBOMs| D(RHTPA)
C -->|Remediate OS| E[Target Fleet Nodes]
C -->|Remediate App| F(Gitea / CI Pipeline)
F -->|Rebuild Container| G(Builder Node)
G --> E
| Component | Role |
|---|---|
| AAP Controller | Executes remediation playbooks and configuration as code |
| AAP EDA Controller | Listens for CVE disclosure events and triggers workflows |
| RHTPA | Stores SBOMs; correlates CVEs to affected packages and images |
| Keycloak | Provides OIDC authentication for RHTPA and AAP |
| Gitea | Hosts application source; receives rebuild triggers via webhooks |
| Target Fleet | RHEL 9 nodes running vulnerable OS packages and application containers |
cve_disclosed event. EDA triggers SBOM Correlate and Mitigate, which queries RHTPA to identify affected hosts and applies mitigations while a patch is not yet available.rhsa_available event when a Red Hat Security Advisory is released. EDA triggers Patch Test and Deploy, which runs dnf update on the affected OS package and re-scans with Grype to confirm remediation.app_dependency_fixed event when an upstream Python/library fix is available. EDA triggers App Dependency Remediate, which sends a rebuild signal to Gitea. The CI pipeline rebuilds the container image with the patched dependency.Tip: Use an LLM to identify which containers need rebuilding.
When a dependency fix arrives, it is rarely obvious which of many container images actually bundle the affected package. The
App Dependency Remediateworkflow can include a task that calls an LLM (via an MCP endpoint or the AAP MCP server) with the patched package name and the full SBOM inventory from RHTPA. The model cross-references the package against every image’s dependency tree and returns a ranked list of containers that need a rebuild – so the CI/CD trigger is scoped to only the affected services rather than rebuilding the entire fleet.A simple pattern using the
ansible.builtin.urimodule:- name: Ask LLM which containers include the affected package ansible.builtin.uri: url: "{{ llm_mcp_endpoint }}/query" method: POST body_format: json body: prompt: > Given the following SBOM inventory: {{ rhtpa_sbom_summary }}. Which container images include the package {{ affected_package }} as a direct or transitive dependency? Return a JSON list of image names only. register: llm_response - name: Trigger rebuild only for affected images ansible.builtin.set_fact: rebuild_targets: "{{ llm_response.json.images }}"
Edit inventory/hosts.yml to map your node IP addresses and roles:
all:
children:
targets:
hosts:
node1:
ansible_host: "{{ vault_node1_host }}"
node_role: target_fleet
environment_type: production
builders:
hosts:
node2:
ansible_host: "{{ vault_node2_host }}"
node_role: rhtpa_builder
rhtpa_enabled: true
Store sensitive values in an Ansible Vault file:
# inventory/group_vars/vault.yml (ansible-vault create)
vault_ansible_user: "your_ssh_user"
vault_ssh_password: "your_ssh_password"
vault_node1_host: "10.0.0.1"
vault_node2_host: "10.0.0.2"
vault_controller_host: "https://aap.example.com"
vault_controller_username: "admin"
vault_controller_password: "aap_password"
vault_controller_token: "aap_oauth_token"
vault_registry_username: "registry_user"
vault_registry_password: "registry_password"
vault_gitea_admin_password: "gitea_secure_password"
Warning: Do not change
demo_cve_idordemo_app_cve_id.These variables must match the CVE IDs embedded in the webhook payload JSON files under
demo/. Changing them without updating the payload files will cause the RHTPA correlation to return empty results.
Install the required collections, then provision all supporting services on Node 2:
ansible-galaxy collection install -r collections/requirements.yml
# Keycloak + RHTPA
ansible-playbook setup/deploy_keycloak.yml -i inventory/hosts.yml --ask-vault-pass
ansible-playbook setup/deploy_rhtpa.yml -i inventory/hosts.yml --ask-vault-pass
# Gitea + act_runner + Lightwell PyPI index
ansible-playbook setup/deploy_gitea.yml -i inventory/hosts.yml --ask-vault-pass
ansible-playbook setup/deploy_pypiserver.yml -i inventory/hosts.yml --ask-vault-pass
ansible-playbook setup/seed_gitea.yml -i inventory/hosts.yml --ask-vault-pass
# Install vulnerable OS package on targets
ansible-playbook setup/prepare_target.yml -i inventory/hosts.yml --ask-vault-pass
# Install syft and grype on builder
ansible-playbook setup/prepare_builder.yml -i inventory/hosts.yml --ask-vault-pass
A single CaC playbook creates all job templates, workflow templates, credentials, and projects in AAP Controller:
ansible-playbook controller/configure.yml --ask-vault-pass
Scan all target nodes and upload their SBOMs to RHTPA before the pipeline runs. This baseline is what RHTPA correlates CVEs against:
ansible-playbook playbooks/sbom_scan_upload.yml -i inventory/hosts.yml --ask-vault-pass
In the AAP EDA Controller UI, create a new Rulebook Activation and deploy rulebooks/cve_lifecycle.yml. The rulebook listens on eda_webhook_port (default 5000) and routes incoming events to the correct AAP workflow:
# rulebooks/cve_lifecycle.yml
- name: CVE Lifecycle Management
hosts: all
sources:
- ansible.eda.webhook:
host: 0.0.0.0
port: "{{ eda_webhook_port | default(5000) }}"
rules:
- name: CVE Disclosed -- Correlate and Mitigate
condition: event.payload.event_type == "cve_disclosed"
action:
run_job_template:
name: "SBOM Correlate and Mitigate"
organization: Default
job_args:
extra_vars:
cve_id: "{{ event.payload.cve_id }}"
cvss_score: "{{ event.payload.cvss_score }}"
- name: RHSA Available -- Patch and Deploy
condition: event.payload.event_type == "rhsa_available"
action:
run_job_template:
name: "Patch Test and Deploy"
organization: Default
job_args:
extra_vars:
rhsa_id: "{{ event.payload.rhsa_id }}"
target_package: "{{ event.payload.package_name }}"
- name: App Dependency Fixed -- Rebuild Container
condition: event.payload.event_type == "app_dependency_fixed"
action:
run_job_template:
name: "App Dependency Remediate"
organization: Default
job_args:
extra_vars:
rebuild_path: "{{ event.payload.rebuild_path | default('cicd') }}"
Configure the EDA rulebook activation with the following settings:
| Field | Value |
|---|---|
| Rulebook | cve_lifecycle.yml |
| Decision Environment | Lightwell EE (built in Step 1) |
| AAP Controller credential | Token with job template execute rights |
| Webhook port | 5000 (or eda_webhook_port from variables) |
Run this command on a target node after the Patch Test and Deploy workflow completes:
ssh root@<node1_ip> "rpm -q python-cryptography"
Expected Output: python-cryptography-36.0.1-6.el9_6 (or the demo_cve_fixed_version from variables).
Check the running container’s dependency version after the App Dependency Remediate workflow completes:
ssh root@<node1_ip> "podman exec config-service pip show pyyaml"
Expected Output: Version: 6.0.2 (or the demo_app_fixed_version from variables).
In the AAP Controller UI navigate to Jobs. Confirm successful executions for:
CVE CorrelationCME Mitigate (if mitigation workflow ran)Patch and VerifyApp Dependency Remediate (if app workflow ran)| Failure Mode | How to Read Logs | Specific Fix |
|---|---|---|
| EDA Webhook Timeout | Check trigger_*.sh output for curl: (7) Failed to connect |
Verify EDA_HOST is exported and the EDA rulebook activation is running on port 5000 |
| Controller CaC Fails | Check controller/configure.yml output for 401 Unauthorized |
Verify vault_controller_token is valid with admin rights |
| Gitea Seed Fails | Check setup/seed_gitea.yml for authentication errors |
Ensure vault_gitea_api_token was generated by deploy_gitea.yml and added to vault.yml |
| RHTPA Upload Fails | Check sbom_scan_upload.yml logs for 403 Forbidden or connection refused |
Verify Node 2 is reachable from the EE and rhtpa_oidc_client_secret matches Keycloak configuration |
| Container Rebuild Fails | Check App Container Rebuild job logs in AAP |
Confirm vault_registry_username and password are correct and the builder node has internet access |
Note: To reset the environment for a fresh pipeline run:
ansible-playbook setup/reset_demo.yml -i inventory/hosts.yml --ask-vault-pass
| Maturity | Description |
|---|---|
| Crawl | Manual triage with SBOM visibility. RHTPA maintains SBOMs for all workloads. Security engineers query RHTPA manually when a CVE is announced, identify affected hosts, and remediate via ad-hoc Ansible playbooks. SBOM coverage replaces guesswork; patching is still human-initiated. |
| Walk | EDA-triggered pipeline with approval gates. Lightwell CVE events trigger EDA automatically. The AAP workflow runs SBOM correlation and OS patching, but an approval node requires a human sign-off before container rebuilds execute. Engineers review the blast radius before allowing CI/CD changes. |
| Run | Fully autonomous CVE-to-patch. Policy-based automation replaces approval gates for well-understood CVE classes (CVSS < 7, OS package only, no app-layer changes). High-severity or container-rebuild paths retain human approval. Mean-time-to-remediation for low-risk patches drops to under 30 minutes with no manual steps. |
This solution closes the manual gap between CVE disclosure and remediation by connecting Lightwell’s security intelligence feed to Red Hat Trusted Profile Analyzer’s SBOM inventory through an event-driven pipeline built on Ansible. When a CVE is announced, EDA routes the event to the appropriate AAP workflow – whether that is correlating the CVE against SBOMs to identify blast radius, patching OS packages via dnf, or triggering a container image rebuild through Gitea CI/CD. Each pipeline stage is governed by AAP, providing RBAC, audit trails, and approval gates. The maturity path moves from SBOM visibility with manual patching through approval-gated automation to fully policy-driven remediation, giving security and platform teams a clear path to reducing mean-time-to-remediation without sacrificing governance.