Files
ansible-testing/methodologies/ansible/playbooks/templates/test_service_check.yml
T

108 lines
5.0 KiB
YAML

---
# ══════════════════════════════════════════════════════════════════════
# TEMPLATE: Service State Check
# ══════════════════════════════════════════════════════════════════════
#
# Uses ansible.builtin.service_facts — no shell command needed.
# service_facts gathers all service states into ansible_facts.services
# as a dict keyed by service name. Prefer this over shelling out to
# systemctl for any service-related check.
#
# IMPORTANT — run service_facts ONCE per suite, not once per test.
# Put this gather task at the TOP of your suite file:
#
# - name: "Gather: Load all service states"
# ansible.builtin.service_facts:
#
# Then each test block below can query ansible_facts.services without
# running additional commands.
#
# Service dict structure (ansible_facts.services['sshd.service']):
# name: 'sshd.service'
# state: 'running' | 'stopped' | 'failed' | 'inactive'
# status: 'enabled' | 'disabled' | 'masked' | 'static' | 'unknown'
#
# Common 'passed' expression patterns:
#
# # Service must be running and enabled:
# 'passed': (
# ansible_facts.services['sshd.service'] is defined and
# ansible_facts.services['sshd.service'].state == 'running' and
# ansible_facts.services['sshd.service'].status == 'enabled'
# ),
#
# # Service must NOT be running (insecure service check):
# 'passed': (
# ansible_facts.services['telnet.socket'] is not defined or
# ansible_facts.services['telnet.socket'].state != 'running'
# ),
#
# # Any of several insecure services must all be absent/inactive:
# 'passed': (
# ['telnet.socket', 'rsh.socket', 'ftp.service']
# | map('extract', ansible_facts.services)
# | select('defined')
# | selectattr('state', 'equalto', 'running')
# | list | length == 0
# ),
#
# ══════════════════════════════════════════════════════════════════════
# ── Put this ONCE at the top of the suite file ───────────────────────
#
# - name: "Gather: Load all service states (suite-wide)"
# ansible.builtin.service_facts:
#
# ── Per-test blocks below ────────────────────────────────────────────
# ── SUITE_ID: Service must be running ───────────────────────────────
- block:
- name: "Evaluate: TEST_ID — SERVICE_NAME is running and enabled"
ansible.builtin.set_fact:
test_results: "{{ test_results + [{
'test_id': 'TEST_ID',
'category': 'FR_NUMBER — CATEGORY_NAME',
'requirement': 'SR X.Y — REQUIREMENT_NAME',
'description': 'SERVICE_NAME shall be running and enabled at boot',
'passed': (
ansible_facts.services['SERVICE_NAME.service'] is defined and
ansible_facts.services['SERVICE_NAME.service'].state == 'running' and
ansible_facts.services['SERVICE_NAME.service'].status == 'enabled'
),
'expected': 'SERVICE_NAME: state=running, status=enabled',
'actual': (
'state=' + ansible_facts.services['SERVICE_NAME.service'].state
+ ', status=' + ansible_facts.services['SERVICE_NAME.service'].status
) if ansible_facts.services['SERVICE_NAME.service'] is defined
else 'SERVICE_NAME.service: not found in service facts',
'severity': 'high',
'remediation': 'systemctl enable --now SERVICE_NAME'
}] }}"
ignore_errors: yes
# ── SUITE_ID: Insecure service must NOT be running ──────────────────
- block:
- name: "Evaluate: TEST_ID — INSECURE_SERVICE_NAME is not running"
ansible.builtin.set_fact:
test_results: "{{ test_results + [{
'test_id': 'TEST_ID',
'category': 'FR_NUMBER — CATEGORY_NAME',
'requirement': 'SR X.Y — REQUIREMENT_NAME',
'description': 'INSECURE_SERVICE_NAME shall be disabled and not running',
'passed': (
ansible_facts.services['INSECURE_SERVICE_NAME.service'] is not defined or
ansible_facts.services['INSECURE_SERVICE_NAME.service'].state != 'running'
),
'expected': 'INSECURE_SERVICE_NAME: absent or not running',
'actual': (
'state=' + ansible_facts.services['INSECURE_SERVICE_NAME.service'].state
) if ansible_facts.services['INSECURE_SERVICE_NAME.service'] is defined
else 'not installed',
'severity': 'critical',
'remediation': 'systemctl disable --now INSECURE_SERVICE_NAME'
}] }}"
ignore_errors: yes