Work in Progress.
Strong technical draft merged from the Satellite CVE remediation contribution. Catalog, title chrome, Summary section order, and maturity framing still need a polish pass before publish. Content quality is already high enough to share with leadership as a WIP.
Security teams typically discover a critical CVE, cross-reference it against a fleet of hosts, look up the fixing erratum, and schedule a maintenance window, before a single package is patched. That gap between detection and remediation is where risk lives, and it grows every day the fleet sits unpatched. Every hour of manual triage is an hour a Critical or Important CVE remains exploitable across the fleet.
This guide demonstrates how to close that gap automatically using Red Hat Satellite, its on-premises Red Hat Lightspeed Vulnerability service, and Event-Driven Ansible (EDA). A routine Satellite operation such as a successful remote execution job on any single host, fires a webhook that triggers a fleet-wide scan for remediable CVEs and installs the fixes, with zero manual intervention and zero data leaving the Satellite server. The CVE data and remediation logic are entirely on-premises so this pattern works in air-gapped environments.
Operational Impact: Medium to High – fleet-wide package installs (see Prerequisites for the per-stage breakdown).
Business Value Drivers:
Technical Value Drivers:
Keeps Satellite admin credentials out of playbooks and rulebooks entirely, using AAP Controller’s credential injection
Red Hat Satellite is a systems management platform for provisioning, configuring, and maintaining RHEL hosts at scale. Among its capabilities is an on-premises Red Hat Lightspeed Vulnerability service: a page in the Satellite Web UI, under Red Hat Lightspeed -> Vulnerability, that cross-references each managed host’s installed packages against CVE data already synced into Satellite. Despite sharing a name with the hosted console.redhat.com Lightspeed Vulnerability dashboard, this is a distinct, fully on-premises service. No host inventory or package list ever leaves the Satellite server.
Two services share the “Lightspeed Vulnerability” name.
Satellite’s Red Hat Lightspeed -> Vulnerability page (used throughout this guide) is served entirely on-premises by Satellite itself. It is a different service from the hosted Red Hat Lightspeed on
console.redhat.com– same brand name, different deployment, and no data leaves your network when you use Satellite’s version.
Satellite also exposes a webhook subsystem: HTTPS callbacks that fire whenever a defined event occurs (a host registers, a content view publishes, a remote execution job succeeds). Combined with Event-Driven Ansible, a webhook becomes an automation trigger. EDA listens for the webhook’s HTTP POST on an Event Stream, evaluates a rulebook’s conditions against the event payload, and launches an AAP Job Template when a rule matches.
For more information on Satellite events that can fire webhooks, see the Satellite documentation on available webhook events.
This is not the same as errata-based remediation.
Satellite’s classic errata view lets an administrator remediate hosts by RHSA/RHBA/RHEA advisory. The Lightspeed Vulnerability service instead presents a CVE-centric view, cross-referenced against Satellite’s Katello content API for the exact erratum and packages that fix each one. Both features remain available in Satellite; this guide automates the CVE-centric path. See the Satellite documentation on patching through errata for the difference in more depth.
Event-Driven Ansible is the bridge that turns a Satellite webhook into governed remediation, without teaching Satellite anything about AAP credentials or Satellite teaching AAP anything about its admin password.
This solution wires together three components so that a single Satellite event triggers a fleet-wide remediation sweep:
The actual event that triggers the proactive remediation workflow does not have to be a remote execution operation, as shown in this solution guide; the use of the remote execution event is arbitrary for demonstration purposes.
As well, the remediation action does not have to result in automatically patching hosts. You can configure other operations that align with your organization’s information security policies. This can range from turning off selected systems, to quarantining selected VLANs, to patching RPMs in servers in test environments, perhaps resulting in smoke tests to check that a patch does not adversely affect the operation of critical systems.
| Persona | Challenge | What They Gain |
|---|---|---|
| IT Ops Engineer / SRE | Manually cross-referencing CVE reports against host inventory, then patching hosts one at a time | A single Satellite event triggers automatic discovery and remediation across every affected host, not just the one that fired the event |
| Automation Architect | Building bespoke scripts to query vulnerability data and errata, and keeping Satellite credentials out of playbooks | A reusable credential-injection pattern and a documented EDA rulebook/Job Template wiring that works fully on-premises |
| IT Manager / Director | Patch compliance SLAs measured in days, with CVEs sitting exploitable while tickets move through a queue | Time-to-remediate measured in minutes for Critical/Important CVEs, with a severity-based policy and full AAP job audit trail |
ansible.controller/ansible.eda collection features used in this guide| Collection | Type | Purpose |
|---|---|---|
| ansible.eda | Certified | EDA event sources - provides the ansible.eda.webhook source plugin that receives the Satellite webhook |
| ansible.controller | Certified | Programmatic creation of the Controller credential type, credential, Project, and Job Template |
| System | Required | Purpose |
|---|---|---|
| Red Hat Satellite 6.19+ | Yes | Host management, on-premises Lightspeed Vulnerability data, Katello errata API, webhook source |
| Custom Execution Environment | Yes | Must include uv on PATH; vulnerability_remediation.py uses a #!/usr/bin/env -S uv run --script shebang, which AAP’s stock EEs do not provide. Built and registered in Step 6 |
| SSH/Machine credential | Yes | The remediation playbook installs packages on each affected host over SSH |
Operational Impact: Medium to High - the closed-loop remediation step installs package updates on every affected host in the fleet. Scope the severity policy (Critical/Important by default) before enabling in production, and pilot on a non-production Satellite organization first.
Never hardcode the Satellite hostname.
Every featured playbook in this guide reads the target Satellite server from a
satellite_hostnamevariable, not a literal URL. Define it once in inventory and it resolves everywhere. The same playbooks work against any Satellite instance without editing YAML.
# inventory/hosts.yml
satellite:
hosts:
satellite01.example.com:
# group_vars/satellite.yml
satellite_hostname: "{{ inventory_hostname }}"
satellite_validate_certs: false # set true once a trusted CA cert is in place
The Job Template passes satellite_hostname (and the injected satellite_username/SATELLITE_PASSWORD from Step 8) into every play that talks to Satellite’s API.
The pipeline runs in two chained stages: the webhook that starts it, and the remediation sweep it triggers.
graph LR
A([Remote Exec Job Succeeds]) --> B[Satellite Webhook]
B --> C[AAP Gateway]
C --> D[EDA Event Stream]
D --> E[Rulebook Activation]
E --> F([Job Template Launched])
graph LR
A([Job Template Launched]) --> B["Unscoped CVE Scan (Satellite)"]
B --> C[Identify All Affected Hosts]
C --> D["Per-Host Rescan (Exact Packages)"]
D --> E["dnf Install (Fleet-Wide)"]
E --> F([Fleet Remediated])
A successful remote execution job on any single host web01.example.com running a routine command, is the trigger. Satellite’s webhook POSTs a JSON payload to AAP’s EDA Event Stream, which validates the Basic Auth credential and delivers the event to a running rulebook Activation. The rulebook’s single rule checks that the job succeeded, logs the event, and launches the Vulnerability Package Finder and Remediator Job Template, passing along the triggering host name for the audit trail only.
The Job Template’s playbook does not stop at the triggering host. It first runs an unscoped scan against Satellite’s Lightspeed Vulnerability and Katello errata APIs to discover every host in the fleet with at least one installable remediation for a Critical or Important CVE. It then re-scans each of those hosts individually to get the exact package list each one needs, and installs those packages via dnf. A webhook from one host is only the trigger; the remediation itself is fleet-wide.
| Stage | Impact | Notes |
|---|---|---|
| 1. Satellite webhook fires | None | Read-only HTTPS callback |
| 2. EDA validates and routes the event | None | Basic Auth check against the Event Stream credential |
| 3. Unscoped CVE scan | None | Read-only query against Satellite’s Vulnerability and Katello APIs |
| 4. Per-host rescan | None | Read-only, narrows the package list per host |
| 5. Install fixed packages | High | Applies package updates fleet-wide via dnf |
Operational Impact: None
Before wiring up any automation, Satellite already knows what is wrong. In the Satellite Web UI, under Red Hat Lightspeed -> Vulnerability, each detected CVE is listed with a severity rating (Critical/Important/Moderate/Low), a CVSS score, and a count of affected systems. Drilling into any CVE shows the full description and exactly which hosts it affects. This data comes entirely from CVE information already synced into Satellite, cross-referenced against each host’s package list reported by insights-client. No data leaves the Satellite server.

Not every CVE is actionable yet. A CVE only becomes remediable once Satellite has synced an erratum that fixes it, which is exactly what the automation in the following steps checks for.
Operational Impact: Low
Satellite’s built-in webhook templates do not work for the remote-execution-success event this guide uses: the default payload template throws an internal error on this event type, and the other built-in template only emits human-readable text, not JSON. A custom ERB template renders real JSON instead:
<%#
name: Satellite Remote Execution Host Job JSON
description: JSON payload for actions.remote_execution.run_host_job_succeeded.event.foreman
snippet: false
model: WebhookTemplate
-%>
<%=
payload({
host_name: @object.host_name,
host_id: @object.host_id,
job_invocation_id: @object.job_invocation_id,
task_label: @object.task.label,
task_state: @object.task.state,
task_result: @object.task.result,
task_started_at: @object.task.started_at,
task_ended_at: @object.task.ended_at
})
-%>
host_name is the field that matters most. It is passed on to AAP as the triggering host for the audit trail. Register the template through Satellite’s API rather than hammer webhook-template create, which errors on a duplicate name:
- name: Create or update the Satellite webhook template
hosts: satellite
connection: local
gather_facts: false
tasks:
- name: Read the ERB template body
ansible.builtin.set_fact:
webhook_template_body: "{{ lookup('file', 'satellite-remote-execution-host-job-json.erb') }}"
- name: Register the webhook template with Satellite
ansible.builtin.uri:
url: "https://{{ satellite_hostname }}/api/webhook_templates"
method: POST
user: "{{ satellite_username }}"
password: "{{ satellite_password }}"
force_basic_auth: true
validate_certs: "{{ satellite_validate_certs }}"
body_format: json
body:
webhook_template:
name: "Satellite Remote Execution Host Job JSON"
template: "{{ webhook_template_body }}"
no_log: true
satellite_hostname and satellite_validate_certs come from the satellite inventory group defined above, not a hardcoded URL. no_log: true keeps the credential-bearing request out of the job’s visible output.
Operational Impact: Low
The webhook needs the Event Stream’s Basic Auth credentials and its current URL, fetched live rather than trusted from a cached copy. The URL’s UUID can drift if the credential is regenerated, and a stale one fails with {"detail":"bad uuid specified"}.
The webhook event name is a common source of confusion.
Satellite’s create-webhook API takes
actions.remote_execution.run_host_job_succeededwith no.event.foremansuffix, even though the info endpoint displays it with that suffix afterward. Query Satellite’s/api/webhooks/eventsendpoint for its own spelling rather than guessing.
- name: Create the Satellite webhook
hosts: satellite
connection: local
gather_facts: false
tasks:
- name: Look up the exact event name Satellite expects
ansible.builtin.uri:
url: "https://{{ satellite_hostname }}/api/webhooks/events"
method: GET
user: "{{ satellite_username }}"
password: "{{ satellite_password }}"
force_basic_auth: true
validate_certs: "{{ satellite_validate_certs }}"
register: webhook_events
no_log: true
- name: Create the webhook, subscribed to the remote execution success event
ansible.builtin.uri:
url: "https://{{ satellite_hostname }}/api/webhooks"
method: POST
user: "{{ satellite_username }}"
password: "{{ satellite_password }}"
force_basic_auth: true
validate_certs: "{{ satellite_validate_certs }}"
body_format: json
body:
webhook:
name: "AAP EDA Remediation Trigger"
target_url: "{{ eda_event_stream_url }}"
http_method: "POST"
webhook_template_id: "{{ webhook_template_id }}"
event: "{{ webhook_events.json | selectattr('name', 'match', '.*run_host_job_succeeded.*') | map(attribute='name') | first }}"
user: "{{ eda_basic_auth_username }}"
password: "{{ eda_basic_auth_password }}"
no_log: true
Both tasks resolve satellite_hostname from inventory and set no_log: true because their request bodies carry the Satellite admin password and the EDA Basic Auth secret.
Operational Impact: None
The rulebook that receives the webhook deliberately uses one rule with two actions, not two separate rules:
---
- name: Satellite Remote Execution Webhook
hosts: all
sources:
- ansible.eda.webhook:
host: 0.0.0.0
port: 5000
name: satellite_webhook
rules:
- name: Log Satellite remote execution success and remediate CVEs
condition: event.payload.task_result == "success"
actions:
- debug:
msg: "Received Satellite webhook: {{ event }}"
- run_job_template:
name: "Vulnerability Package Finder and Remediator"
organization: "Default"
job_args:
extra_vars:
host_name: "{{ event.payload.host_name }}"
Why one rule instead of two.
The
ansible-rulebookengine fires at most one rule per event. The first matching rule consumes it. A separate catch-all logging rule listed first would swallow every event, and a remediation rule behind it would never fire. Running the debug log and therun_job_templatecall as two actions of the same rule guarantees both happen.
Operational Impact: High (install phase only)
The Job Template runs find_and_remediate.yml, which performs an unscoped scan to discover which hosts have at least one installable remediation, then a per-host rescan for the exact package list, then installs the fixes. find_and_remediate.yml shells out to vulnerability_remediation.py, a standalone uv script; see rhel-labs/satellite-vulnerability-remediation for its full source, CLI flag reference, and installation instructions:
- name: Find CVE remediations for every affected host
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Scan Satellite for all remediable CVEs and the hosts they affect
ansible.builtin.command:
cmd: >-
python3 vulnerability_remediation.py
--satellite https://{{ satellite_hostname }}
--username {{ satellite_username }}
--insecure
chdir: "{{ playbook_dir }}"
register: scan_all
changed_when: false
no_log: true
- name: Build the set of hosts with at least one installable remediation
ansible.builtin.set_fact:
target_hosts: >-
{{ (scan_all.stdout | from_json)
| selectattr("has_remediation", "defined")
| selectattr("has_remediation")
| map(attribute="affected_systems")
| sum(start=[])
| unique }}
- name: Re-scan each affected host for the exact packages it needs
ansible.builtin.command:
cmd: >-
python3 vulnerability_remediation.py
--satellite https://{{ satellite_hostname }}
--username {{ satellite_username }}
--host {{ item }}
--insecure
chdir: "{{ playbook_dir }}"
loop: "{{ target_hosts }}"
register: per_host_scans
changed_when: false
no_log: true
- name: Hand each affected host and its package list to the next play
ansible.builtin.add_host:
name: "{{ item.item }}"
groups: affected_hosts
packages_to_install: >-
{{ (item.stdout | from_json)
| selectattr("packages_to_install_on_host", "defined")
| map(attribute="packages_to_install_on_host")
| select("truthy")
| sum(start=[])
| unique }}
loop: "{{ per_host_scans.results }}"
loop_control:
label: "{{ item.item }}"
- name: Install the fixed packages on every affected host
hosts: affected_hosts
become: true
gather_facts: false
tasks:
- name: Install/upgrade each package that remediates a found CVE
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop: "{{ hostvars[inventory_hostname].packages_to_install }}"
when: hostvars[inventory_hostname].packages_to_install | length > 0
Both command tasks resolve satellite_hostname from inventory rather than a literal URL, and both set no_log: true because the script’s exit output can echo the --username argument back in error messages. The password never appears on the command line. The controller injects it as the SATELLITE_PASSWORD environment variable (Step 8), and vulnerability_remediation.py reads it from there.
vulnerability_remediation.py (behind the scenes) logs in to Satellite and asks two on-premises APIs a question each: the Lightspeed Vulnerability service answers “what CVEs affect my hosts?”, and the Katello content API answers “which erratum and packages fix each one?”. By default it reports only CVEs rated Critical or Important. Widening that policy means passing additional --severity values.
Operational Impact: Low
vulnerability_remediation.py has a #!/usr/bin/env -S uv run --script shebang, and AAP’s stock Execution Environments do not ship uv. The Job Template in Step 8 needs a custom Execution Environment built on top of the supported base image:
FROM registry.redhat.io/ansible-automation-platform-25/ee-supported-rhel9:latest
RUN pip3 install --no-cache-dir uv
Build the image, tag it for a registry AAP’s container runtime can reach, and push it. EE_REGISTRY_HOSTNAME is your own container registry, not a Satellite or AAP hostname:
export EE_REGISTRY_HOSTNAME=registry.example.com
podman build -t ee-vuln-finder -f Containerfile .
podman tag ee-vuln-finder:latest "$EE_REGISTRY_HOSTNAME/ee-vuln-finder:latest"
podman push "$EE_REGISTRY_HOSTNAME/ee-vuln-finder:latest"
Then register it in Controller, using the same ansible.controller collection as every other Controller object in this guide, rather than a raw API call:
- name: Register the custom Execution Environment in Controller
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Register the Vulnerability Finder Execution Environment
ansible.controller.execution_environment:
name: "Vulnerability Finder EE"
image: "{{ ee_registry_hostname }}/ee-vuln-finder:latest"
pull: missing
state: present
ee_registry_hostname is an Ansible variable (define it in group_vars, the same way as satellite_hostname) with the same value as the EE_REGISTRY_HOSTNAME shell variable used above – one is consumed by podman, the other by the Controller module.
The image must be reachable from Controller’s container runtime, not just your workstation.
Controller pulls this image from the registry named in
image:at job launch time, so that registry (and any TLS/auth it requires) must be reachable from the Controller node itself.pull: missingtells Controller to reuse the local image once cached, rather than re-pulling it on every job run.
Operational Impact: None
find_and_remediate.yml and vulnerability_remediation.py need to reach the Job Template as files, not just as names in a table. A Controller Project is what makes that happen: it’s a git checkout Controller keeps in sync, and the Job Template’s playbook: field resolves against that checkout. Both files – along with the Containerfile from Step 6 – live together in rhel-labs/satellite-vulnerability-remediation:
- name: Create the Controller Project for the remediation playbook
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Create the Vulnerability Package Finder Project
ansible.controller.project:
name: "Vulnerability Package Finder"
organization: Default
scm_type: git
scm_url: "https://github.com/rhel-labs/satellite-vulnerability-remediation"
scm_update_on_launch: true
state: present
scm_update_on_launch: truekeeps the Project current without a manual sync step.Every time the Job Template launches, Controller re-syncs this Project first, pulling the latest
find_and_remediate.ymlbefore running it. For a private fork instead of a public repo, add acredential:field pointing at a Source Control credential.
Operational Impact: Low
The playbook needs Satellite admin credentials, but neither the rulebook nor the playbook should hold the password in plaintext. A custom Controller credential type solves this with injectors:
- name: Create the Satellite API credential in Controller
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Define the Satellite API credential type
ansible.controller.credential_type:
name: Satellite API Credentials
kind: cloud
inputs:
fields:
- id: username
type: string
label: Username
- id: password
type: string
label: Password
secret: true
required:
- username
- password
injectors:
extra_vars:
satellite_username: !unsafe '{{ username }}'
env:
SATELLITE_PASSWORD: !unsafe '{{ password }}'
state: present
- name: Create the Satellite admin credential
ansible.controller.credential:
name: Satellite Admin (API)
organization: Default
credential_type: Satellite API Credentials
inputs:
username: admin
password: "{{ vaulted_satellite_password }}"
state: present
At launch time, Controller hands the username to the playbook as the satellite_username extra var and the password as the SATELLITE_PASSWORD environment variable. This is exactly what vulnerability_remediation.py consumes. The password is never written into the playbook, the rulebook, or the repository.
!unsafeis required, not decoration.
{{ username }}and{{ password }}are Controller’s own templates, filled in at launch time. Without!unsafe, Ansible tries to resolve them while creating the credential type and fails on an undefined variable.
Job Template configuration:
| Field | Value |
|---|---|
| Name | Vulnerability Package Finder and Remediator |
| Project | Vulnerability Package Finder (created in Step 7) |
| Playbook | find_and_remediate.yml |
| Execution Environment | Vulnerability Finder EE (built and registered in Step 6) |
| Credentials | Satellite Admin (API), Machine credential (SSH, become: true) |
| Ask Variables on Launch | true - required so the rulebook can pass host_name as an extra var |
RBAC: Scope roles tightly.
Assign the
Executerole on this Job Template only to the automation team responsible for patch compliance. The Machine credential attached here connects asrooton every managed host, soAdminon the Job Template should be limited to the automation architect team.
Missing the Machine credential is a common failure.
The scan plays run
hosts: localhostand always succeed. The install play runs againstaffected_hostsover SSH. Without a Machine credential attached, it fails withPermission denied (publickey).
Trigger a routine remote execution job against any single managed host. This is the same kind of job Satellite runs for day-to-day administration. The only difference is that a successful run of one now fires the webhook into AAP:
hammer job-invocation create \
--job-template "Run Command - Ansible Default" \
--search-query "name = web01.example.com" \
--inputs "command=echo trigger-remediation"
Before triggering, both web01.example.com and web02.example.com report the vulnerable package versions:
$ rpm -q openssl openssl-libs gnutls tar
openssl-3.5.1-4.el10_1.x86_64
openssl-libs-3.5.1-4.el10_1.x86_64
gnutls-3.8.10-2.el10.x86_64
tar-1.35-7.el10.x86_64

Watch the Vulnerability Package Finder and Remediator Job Template launch and complete. Once it finishes, both hosts show the patched OpenSSL packages, even though only web01.example.com fired the webhook:
$ rpm -q openssl openssl-libs gnutls tar
openssl-3.5.1-7.el10_1.x86_64
openssl-libs-3.5.1-7.el10_1.x86_64
gnutls-3.8.10-2.el10.x86_64
tar-1.35-7.el10.x86_64
| Package | Before | After | Result |
|---|---|---|---|
openssl |
3.5.1-4.el10_1 |
3.5.1-7.el10_1 |
Upgraded by RHSA-2026:1472 (Important) |
openssl-libs |
3.5.1-4.el10_1 |
3.5.1-7.el10_1 |
Upgraded by RHSA-2026:1472 (Important) |
gnutls |
3.8.10-2.el10 |
3.8.10-2.el10 |
Not remediated – Moderate, below threshold |
tar |
1.35-7.el10 |
1.35-7.el10 |
Not remediated – Moderate, below threshold |
Only the Critical/Important CVE (CVE-2025-15467, CVSS 9.8) was auto-remediated. The gnutls and tar errata are Moderate severity, below this policy’s default threshold. That is the policy working as intended and it is not a gap.
The Job Template’s PLAY RECAP shows one line per affected host. A host already up to date shows skipped; a host that needed patches shows changed:
PLAY RECAP *********************************************************************
web01.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0
web02.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0
Finally, run insights-client on both hosts so Satellite’s own package inventory catches up. rpm -q reflects the change instantly, but the Lightspeed Vulnerability dashboard only clears the CVE once Satellite re-matches against freshly uploaded package data:
insights-client
Reload Red Hat Lightspeed -> Vulnerability in the Satellite Web UI. The Important-severity OpenSSL CVE that previously showed Systems Affected = 2 no longer appears:

| Symptom | Likely Cause | Fix |
|---|---|---|
Webhook template throws a Ruby error in production.log |
Using a built-in webhook template instead of the custom JSON one | Check /var/log/foreman/production.log for EventSubscriber errors; re-register the custom ERB template from Step 2 |
Satellite webhook rejected with {"detail":"bad uuid specified"} |
Event Stream URL is stale (credential was regenerated after the webhook was created) | Re-fetch the current Event Stream URL from EDA’s API and recreate the webhook |
events_received counter on the Event Stream never increments |
Basic Auth mismatch, or the webhook event name is wrong | Verify credentials; confirm the event name via /api/webhooks/events rather than guessing the .event.foreman suffix |
Job Template fails immediately with a missing uv error |
Stock AAP Execution Environment used instead of the custom Vulnerability Finder EE |
Rebuild and push the image from Step 6’s Containerfile, then confirm the Job Template’s Execution Environment field points at Vulnerability Finder EE |
Install play fails with Permission denied (publickey) |
Machine credential not attached to the Job Template | Attach the SSH Machine credential used to connect as root on managed hosts |
| Scan runs but reports no remediable CVEs | Severity filter excludes the CVE, or Satellite hasn’t synced the fixing erratum yet | Confirm the CVE is rated Critical or Important, and that Satellite has synced the corresponding erratum |
| Vulnerability dashboard still shows the CVE after remediation | insights-client hasn’t run on all affected hosts yet |
Run insights-client on every affected host; the dashboard only clears once every affected host’s inventory is refreshed |
| Maturity | Description |
|---|---|
| Crawl | Manual, errata-based remediation via the Satellite Web UI - an administrator reviews the Vulnerability dashboard and applies fixes by hand |
| Walk | Severity-scoped remediation with a human approval gate or a pilot organization - the webhook still triggers the scan automatically, but an AAP Workflow Template approval node (or scoping the Job Template to a non-production Satellite organization) requires a human to approve before the install play runs |
| Run | Policy-driven, fully automated auto-install - the pattern this guide builds end-to-end: webhook triggers the scan, Critical/Important fixes install fleet-wide with no approval gate, within a defined change window |
This guide builds the Run stage.
Steps 1-8 wire up fully automated, no-approval-gate remediation. To run this at Walk instead, insert an AAP Workflow Template approval node between the scan and the install play, or point the Job Template’s inventory at a pilot/non-production Satellite organization first. Restrict the webhook’s remote-execution trigger to a defined change window before promoting to Run.
A single Satellite event now triggers fleet-wide discovery and remediation of Critical and Important CVEs, cutting time-to-remediate from days of manual triage to minutes. Satellite’s on-premises Lightspeed Vulnerability service and Katello errata API never require data to leave the network, Controller’s credential injectors keep the Satellite admin password out of every playbook and rulebook, and every remediation leaves a full AAP job audit trail.
vulnerability_remediation.py, the CVE/errata lookup script used in Step 5