# service_disable_playbook.yml
# 메인 플레이북: 서비스 상태 체크 후 조건부 비활성화 수행
---
- name: Service Status Check and Conditional Disable
hosts: all
gather_facts: yes
become: yes
serial: 5 # 5개 노드씩 순차 처리하여 안정성 확보
vars:
# 비활성화 대상 서비스 설정 (기본값: false로 안전하게 설정)
disable_auditd: false
disable_mlocate: false
disable_ntp_services: false
# 시간 동기화 관련 설정
max_time_drift_seconds: 30
ntp_server_pool: "pool.ntp.org"
# 백업 및 로그 설정
backup_configs: true
log_changes: true
tasks:
# Phase 1: 현재 상태 체크 (기존 플레이북 재사용)
- name: Include service status check
include_tasks: service_status_check_complete.yml
# Phase 2: 조건부 서비스 비활성화
- name: Include conditional service disable tasks
include_tasks: conditional_service_disable.yml
# Phase 3: 시간 동기화 처리
- name: Include time synchronization tasks
include_tasks: time_sync_correction.yml
when: node_result.ntp.ntp_synchronized == false or
(time_reference is defined and
((node_result.current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ')) -
(time_reference | to_datetime('%Y-%m-%dT%H:%M:%SZ'))).total_seconds() | abs > max_time_drift_seconds)
# Phase 4: 최종 검증 및 보고
- name: Include final verification
include_tasks: final_verification.yml
# conditional_service_disable.yml
# 조건부 서비스 비활성화 태스크
---
# ========================================
# AUDITD 서비스 비활성화
# ========================================
- name: Check if auditd disable is requested and service is active
set_fact:
should_disable_auditd: "{{ disable_auditd and node_result.auditd.service_exists and node_result.auditd.running }}"
- block:
- name: Create backup of auditd configuration
copy:
src: /etc/audit/auditd.conf
dest: /etc/audit/auditd.conf.backup.{{ ansible_date_time.epoch }}
remote_src: yes
backup: yes
when: backup_configs and node_result.auditd.service_exists
- name: Log auditd disable action
lineinfile:
path: /var/log/ansible_service_changes.log
create: yes
line: "{{ ansible_date_time.iso8601 }} - {{ inventory_hostname }} - Disabling auditd service (was {{ node_result.auditd.status }})"
when: log_changes
- name: Stop auditd service
systemd:
name: auditd
state: stopped
enabled: no
register: auditd_disable_result
- name: Mask auditd service to prevent accidental restart
systemd:
name: auditd
masked: yes
when: auditd_disable_result is succeeded
- name: Verify auditd is stopped
systemd:
name: auditd
register: auditd_final_status
- name: Display auditd disable result
debug:
msg: |
✅ AUDITD DISABLED SUCCESSFULLY on {{ inventory_hostname }}
- Previous Status: {{ node_result.auditd.status }}
- Current Status: {{ auditd_final_status.status.ActiveState }}
- Service Masked: Yes
{% if backup_configs %}
- Config Backup: /etc/audit/auditd.conf.backup.{{ ansible_date_time.epoch }}
{% endif %}
when: should_disable_auditd
- name: Skip auditd disable - not requested or not running
debug:
msg: |
⏭️ SKIPPING AUDITD DISABLE on {{ inventory_hostname }}
- Disable Requested: {{ disable_auditd }}
- Service Exists: {{ node_result.auditd.service_exists }}
- Currently Running: {{ node_result.auditd.running }}
- Current Status: {{ node_result.auditd.status }}
when: not should_disable_auditd
# ========================================
# MLOCATE 서비스 비활성화
# ========================================
- name: Check if mlocate disable is requested and timer is active
set_fact:
should_disable_mlocate: "{{ disable_mlocate and node_result.mlocate.timer_exists and node_result.mlocate.timer_running }}"
- block:
- name: Log mlocate disable action
lineinfile:
path: /var/log/ansible_service_changes.log
create: yes
line: "{{ ansible_date_time.iso8601 }} - {{ inventory_hostname }} - Disabling mlocate timer (was {{ node_result.mlocate.timer_status }})"
when: log_changes
- name: Stop mlocate updatedb timer
systemd:
name: "{{ node_result.mlocate.timer_service }}"
state: stopped
enabled: no
register: mlocate_disable_result
- name: Mask mlocate timer to prevent accidental restart
systemd:
name: "{{ node_result.mlocate.timer_service }}"
masked: yes
when: mlocate_disable_result is succeeded
- name: Remove mlocate database if requested
file:
path: "{{ node_result.mlocate.db_path }}"
state: absent
when: mlocate_disable_result is succeeded and node_result.mlocate.db_exists
register: mlocate_db_removed
- name: Verify mlocate timer is stopped
systemd:
name: "{{ node_result.mlocate.timer_service }}"
register: mlocate_final_status
- name: Display mlocate disable result
debug:
msg: |
✅ MLOCATE DISABLED SUCCESSFULLY on {{ inventory_hostname }}
- Timer Service: {{ node_result.mlocate.timer_service }}
- Previous Status: {{ node_result.mlocate.timer_status }}
- Current Status: {{ mlocate_final_status.status.ActiveState }}
- Service Masked: Yes
{% if mlocate_db_removed is defined and mlocate_db_removed.changed %}
- Database Removed: {{ node_result.mlocate.db_path }}
{% endif %}
when: should_disable_mlocate
- name: Skip mlocate disable - not requested or not running
debug:
msg: |
⏭️ SKIPPING MLOCATE DISABLE on {{ inventory_hostname }}
- Disable Requested: {{ disable_mlocate }}
- Timer Exists: {{ node_result.mlocate.timer_exists }}
- Timer Running: {{ node_result.mlocate.timer_running }}
- Timer Status: {{ node_result.mlocate.timer_status }}
when: not should_disable_mlocate
# ========================================
# NTP 서비스 비활성화 (선택적)
# ========================================
- name: Check if NTP disable is requested and service is active
set_fact:
should_disable_ntp: "{{ disable_ntp_services and node_result.ntp.active_service != 'none' }}"
- block:
- name: Create backup of time service configurations
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
remote_src: yes
backup: yes
loop:
- { src: "/etc/chrony.conf", dest: "/etc/chrony.conf.backup.{{ ansible_date_time.epoch }}" }
- { src: "/etc/ntp.conf", dest: "/etc/ntp.conf.backup.{{ ansible_date_time.epoch }}" }
when: backup_configs
failed_when: false # 파일이 없을 수 있으므로 실패 무시
- name: Log NTP disable action
lineinfile:
path: /var/log/ansible_service_changes.log
create: yes
line: "{{ ansible_date_time.iso8601 }} - {{ inventory_hostname }} - Disabling {{ node_result.ntp.active_service }} service"
when: log_changes
- name: Stop active NTP service
systemd:
name: "{{ node_result.ntp.active_service }}"
state: stopped
enabled: no
register: ntp_disable_result
- name: Mask NTP service to prevent accidental restart
systemd:
name: "{{ node_result.ntp.active_service }}"
masked: yes
when: ntp_disable_result is succeeded
- name: Verify NTP service is stopped
systemd:
name: "{{ node_result.ntp.active_service }}"
register: ntp_final_status
- name: Display NTP disable result
debug:
msg: |
✅ NTP SERVICE DISABLED SUCCESSFULLY on {{ inventory_hostname }}
- Service: {{ node_result.ntp.active_service }}
- Current Status: {{ ntp_final_status.status.ActiveState }}
- Service Masked: Yes
⚠️ WARNING: Time synchronization is now disabled!
when: should_disable_ntp
- name: Skip NTP disable - not requested or no active service
debug:
msg: |
⏭️ SKIPPING NTP DISABLE on {{ inventory_hostname }}
- Disable Requested: {{ disable_ntp_services }}
- Active Service: {{ node_result.ntp.active_service }}
when: not should_disable_ntp
# ========================================
# 서비스 비활성화 요약
# ========================================
- name: Create disable action summary
set_fact:
disable_summary:
hostname: "{{ inventory_hostname }}"
auditd_disabled: "{{ should_disable_auditd | default(false) }}"
mlocate_disabled: "{{ should_disable_mlocate | default(false) }}"
ntp_disabled: "{{ should_disable_ntp | default(false) }}"
timestamp: "{{ ansible_date_time.iso8601 }}"
actions_taken:
- "{{ 'auditd stopped and masked' if should_disable_auditd else 'auditd unchanged' }}"
- "{{ 'mlocate timer stopped and masked' if should_disable_mlocate else 'mlocate unchanged' }}"
- "{{ node_result.ntp.active_service + ' stopped and masked' if should_disable_ntp else 'NTP unchanged' }}"
- name: Display per-node disable summary
debug:
msg: |
================================================
🔧 SERVICE DISABLE SUMMARY - {{ inventory_hostname }}
================================================
Timestamp: {{ disable_summary.timestamp }}
📊 Actions Taken:
{% for action in disable_summary.actions_taken %}
- {{ action }}
{% endfor %}
📋 Current Service States:
{% if disable_summary.auditd_disabled %}
✅ auditd: DISABLED AND MASKED
{% else %}
⏭️ auditd: {{ node_result.auditd.status }} (unchanged)
{% endif %}
{% if disable_summary.mlocate_disabled %}
✅ mlocate: DISABLED AND MASKED
{% else %}
⏭️ mlocate: {{ node_result.mlocate.timer_status }} (unchanged)
{% endif %}
{% if disable_summary.ntp_disabled %}
⚠️ {{ node_result.ntp.active_service }}: DISABLED AND MASKED
{% else %}
⏭️ NTP: {{ node_result.ntp.active_service }} (unchanged)
{% endif %}
- name: Save disable action log
copy:
content: "{{ disable_summary | to_nice_json }}"
dest: "/tmp/disable_actions_{{ inventory_hostname }}.json"
delegate_to: localhost
# time_sync_correction.yml
# 시간 동기화 문제 해결 태스크
---
# ========================================
# 시간 차이 분석 및 교정
# ========================================
- name: Calculate time drift from reference
set_fact:
time_drift_seconds: |
{% if time_reference is defined %}
{% set ref_time = time_reference | to_datetime('%Y-%m-%dT%H:%M:%SZ') %}
{% set node_time = node_result.current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') %}
{{ (node_time - ref_time).total_seconds() }}
{% else %}
0
{% endif %}
- name: Determine time correction strategy
set_fact:
time_correction_needed: "{{ (time_drift_seconds | float | abs) > max_time_drift_seconds }}"
drift_level: |
{% set drift = time_drift_seconds | float | abs %}
{% if drift > 300 %}
CRITICAL
{% elif drift > 60 %}
HIGH
{% elif drift > 30 %}
MODERATE
{% else %}
MINOR
{% endif %}
- name: Display time analysis
debug:
msg: |
🕐 TIME DRIFT ANALYSIS - {{ inventory_hostname }}
================================================
Current Time: {{ node_result.current_time }}
Reference Time: {{ time_reference | default('N/A') }}
Time Drift: {{ time_drift_seconds | float | round(2) }}s
Drift Level: {{ drift_level }}
Correction Needed: {{ time_correction_needed }}
NTP Synchronized: {{ node_result.ntp.ntp_synchronized }}
Active NTP Service: {{ node_result.ntp.active_service }}
# ========================================
# NTP 서비스 상태 확인 및 수정
# ========================================
- name: Fix NTP synchronization issues
block:
- name: Install chrony if no time service is available
package:
name: chrony
state: present
when: node_result.ntp.active_service == 'none'
register: chrony_installed
- name: Configure chrony with reliable NTP servers
template:
src: chrony.conf.j2
dest: /etc/chrony.conf
backup: yes
notify: restart chronyd
when: node_result.ntp.chronyd_exists or chrony_installed is defined
- name: Start and enable chronyd
systemd:
name: chronyd
state: started
enabled: yes
when: node_result.ntp.chronyd_exists or chrony_installed is defined
register: chronyd_started
- name: Force immediate time synchronization (gradual)
command: chrony -q 'server {{ ntp_server_pool }} iburst'
when: time_correction_needed and drift_level in ['MINOR', 'MODERATE']
register: gradual_sync_result
failed_when: false
- name: Force immediate time synchronization (step for critical drift)
block:
- name: Stop chronyd for manual time step
systemd:
name: chronyd
state: stopped
- name: Force time step with chronyd
command: chronyd -q 'server {{ ntp_server_pool }} iburst'
register: step_sync_result
- name: Start chronyd after manual sync
systemd:
name: chronyd
state: started
when: time_correction_needed and drift_level == 'CRITICAL'
- name: Wait for synchronization to stabilize
wait_for:
timeout: 30
when: chronyd_started is changed or gradual_sync_result is defined or step_sync_result is defined
- name: Verify time synchronization after correction
command: timedatectl status
register: time_verify_result
- name: Get updated chrony status
command: chronyc tracking
register: chrony_tracking_updated
failed_when: false
when: node_result.ntp.chronyd_exists or chrony_installed is defined
- name: Display time correction results
debug:
msg: |
✅ TIME SYNCHRONIZATION CORRECTION COMPLETED
================================================
Previous Drift: {{ time_drift_seconds | float | round(2) }}s
Correction Method: {{ 'Step' if drift_level == 'CRITICAL' else 'Gradual' }}
Updated Status:
{{ time_verify_result.stdout }}
{% if chrony_tracking_updated.stdout is defined %}
Chrony Tracking:
{{ chrony_tracking_updated.stdout }}
{% endif %}
when: time_correction_needed or not node_result.ntp.ntp_synchronized
# ========================================
# 시간 동기화 모니터링 설정
# ========================================
- name: Setup time synchronization monitoring
block:
- name: Create time sync monitoring script
copy:
content: |
#!/bin/bash
# Time synchronization monitoring script
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
LOG_FILE="/var/log/time_sync_monitor.log"
MAX_DRIFT=30
# Get current time sync status
TIMEDATECTL_OUTPUT=$(timedatectl status)
NTP_SYNC=$(echo "$TIMEDATECTL_OUTPUT" | grep "NTP synchronized" | awk '{print $3}')
if [[ "$NTP_SYNC" != "yes" ]]; then
echo "$(date): WARNING - NTP not synchronized" >> $LOG_FILE
systemctl restart chronyd
fi
# Check chrony tracking if available
if command -v chronyc &> /dev/null; then
CHRONY_TRACKING=$(chronyc tracking)
OFFSET=$(echo "$CHRONY_TRACKING" | grep "Last offset" | awk '{print $4}')
if [[ $(echo "$OFFSET > $MAX_DRIFT" | bc -l) -eq 1 ]] 2>/dev/null; then
echo "$(date): WARNING - Time offset too large: ${OFFSET}s" >> $LOG_FILE
chrony -q 'server {{ ntp_server_pool }} iburst'
fi
fi
dest: /usr/local/bin/time_sync_monitor.sh
mode: '0755'
- name: Create cron job for time sync monitoring
cron:
name: "Time synchronization monitoring"
minute: "*/15"
job: "/usr/local/bin/time_sync_monitor.sh"
user: root
- name: Create systemd timer for time sync monitoring (alternative)
copy:
content: |
[Unit]
Description=Time Sync Monitor Timer
[Timer]
OnCalendar=*:0/15
Persistent=true
[Install]
WantedBy=timers.target
dest: /etc/systemd/system/time-sync-monitor.timer
- name: Create systemd service for time sync monitoring
copy:
content: |
[Unit]
Description=Time Sync Monitor Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/time_sync_monitor.sh
User=root
dest: /etc/systemd/system/time-sync-monitor.service
- name: Enable time sync monitoring timer
systemd:
name: time-sync-monitor.timer
enabled: yes
state: started
daemon_reload: yes
when: time_correction_needed and node_result.ntp.ntp_synchronized
# ========================================
# 시간 동기화 권장사항
# ========================================
- name: Generate time sync recommendations
set_fact:
time_recommendations: |
{% set drift = time_drift_seconds | float | abs %}
{% if drift > 300 %}
CRITICAL TIME DRIFT ({{ drift | round(2) }}s):
1. 즉시 수동 시간 동기화 수행
2. NTP 서버 연결 상태 확인
3. 방화벽 설정 점검 (UDP 123 포트)
4. 네트워크 연결성 확인
5. 시스템 클럭 하드웨어 점검 권장
{% elif drift > 60 %}
HIGH TIME DRIFT ({{ drift | round(2) }}s):
1. NTP 서비스 재시작 수행
2. 더 가까운 NTP 서버로 변경 고려
3. 정기적인 시간 동기화 모니터링 설정
{% elif drift > 30 %}
MODERATE TIME DRIFT ({{ drift | round(2) }}s):
1. NTP 동기화 강제 수행
2. 시간 동기화 빈도 증가
{% else %}
MINOR TIME DRIFT ({{ drift | round(2) }}s):
1. 정상 범위 내 차이
2. 정기 모니터링 유지
{% endif %}
- name: Display time sync recommendations
debug:
msg: |
================================================
🔧 TIME SYNCHRONIZATION RECOMMENDATIONS
================================================
Host: {{ inventory_hostname }}
{{ time_recommendations }}
추가 점검 사항:
- 네트워크 지연: ping {{ ntp_server_pool }}
- NTP 포트 확인: netstat -ulnp | grep :123
- 시스템 로그: journalctl -u chronyd -f
- 하드웨어 클럭: hwclock --show
when: time_correction_needed
# final_verification.yml
# 최종 검증 및 보고서 생성
---
# ========================================
# 변경 후 서비스 상태 재검증
# ========================================
- name: Re-check auditd status after changes
systemd:
name: auditd
register: auditd_final_check
failed_when: false
when: node_result.auditd.service_exists
- name: Re-check mlocate timer status after changes
systemd:
name: "{{ node_result.mlocate.timer_service }}"
register: mlocate_final_check
failed_when: false
when: node_result.mlocate.timer_exists
- name: Re-check NTP status after changes
command: timedatectl status
register: ntp_final_check
failed_when: false
- name: Get updated chrony status
command: chronyc tracking
register: chrony_final_check
failed_when: false
when: node_result.ntp.chronyd_exists
- name: Get current time after changes
setup:
filter: ansible_date_time
register: final_time_info
# ========================================
# 최종 상태 분석
# ========================================
- name: Analyze final service states
set_fact:
final_verification:
hostname: "{{ inventory_hostname }}"
timestamp: "{{ final_time_info.ansible_facts.ansible_date_time.iso8601 }}"
verification_status:
auditd:
requested_disable: "{{ disable_auditd | default(false) }}"
current_status: "{{ auditd_final_check.status.ActiveState if auditd_final_check.status is defined else 'N/A' }}"
is_masked: "{{ auditd_final_check.status.UnitFileState == 'masked' if auditd_final_check.status is defined else false }}"
disable_successful: "{{ (disable_auditd | default(false)) and (auditd_final_check.status.ActiveState == 'inactive' if auditd_final_check.status is defined else false) }}"
mlocate:
requested_disable: "{{ disable_mlocate | default(false) }}"
current_status: "{{ mlocate_final_check.status.ActiveState if mlocate_final_check.status is defined else 'N/A' }}"
is_masked: "{{ mlocate_final_check.status.UnitFileState == 'masked' if mlocate_final_check.status is defined else false }}"
disable_successful: "{{ (disable_mlocate | default(false)) and (mlocate_final_check.status.ActiveState == 'inactive' if mlocate_final_check.status is defined else false) }}"
ntp:
sync_status: "{{ 'synchronized' if 'NTP synchronized: yes' in ntp_final_check.stdout else 'not synchronized' }}"
current_time: "{{ final_time_info.ansible_facts.ansible_date_time.iso8601 }}"
chrony_tracking: "{{ chrony_final_check.stdout if chrony_final_check.stdout is defined else 'N/A' }}"
# ========================================
# 최종 검증 보고서
# ========================================
- name: Display final verification report
debug:
msg: |
================================================
📋 FINAL VERIFICATION REPORT
================================================
Host: {{ final_verification.hostname }}
Verification Time: {{ final_verification.timestamp }}
🔍 AUDITD VERIFICATION:
{% if final_verification.verification_status.auditd.requested_disable %}
- Disable Requested: ✅ YES
- Current Status: {{ final_verification.verification_status.auditd.current_status }}
- Service Masked: {{ '✅ YES' if final_verification.verification_status.auditd.is_masked else '❌ NO' }}
- Disable Success: {{ '✅ SUCCESS' if final_verification.verification_status.auditd.disable_successful else '❌ FAILED' }}
{% else %}
- Disable Requested: ⏭️ NO (Service Unchanged)
- Current Status: {{ final_verification.verification_status.auditd.current_status }}
{% endif %}
📍 MLOCATE VERIFICATION:
{% if final_verification.verification_status.mlocate.requested_disable %}
- Disable Requested: ✅ YES
- Current Status: {{ final_verification.verification_status.mlocate.current_status }}
- Service Masked: {{ '✅ YES' if final_verification.verification_status.mlocate.is_masked else '❌ NO' }}
- Disable Success: {{ '✅ SUCCESS' if final_verification.verification_status.mlocate.disable_successful else '❌ FAILED' }}
{% else %}
- Disable Requested: ⏭️ NO (Service Unchanged)
- Current Status: {{ final_verification.verification_status.mlocate.current_status }}
{% endif %}
🕐 NTP/TIME VERIFICATION:
- Current Time: {{ final_verification.verification_status.ntp.current_time }}
- Sync Status: {{ '✅ SYNCHRONIZED' if final_verification.verification_status.ntp.sync_status == 'synchronized' else '⚠️ NOT SYNCHRONIZED' }}
{% if final_verification.verification_status.ntp.chrony_tracking != 'N/A' %}
- Chrony Details:
{{ final_verification.verification_status.ntp.chrony_tracking | indent(4) }}
{% endif %}
- name: Save final verification report
copy:
content: "{{ final_verification | to_nice_json }}"
dest: "/tmp/final_verification_{{ inventory_hostname }}.json"
delegate_to: localhost
# ========================================
# 전체 클러스터 최종 요약 (run_once)
# ========================================
- name: Collect all verification reports
find:
paths: /tmp
patterns: "final_verification_*.json"
register: verification_files
delegate_to: localhost
run_once: true
- name: Read all verification files
slurp:
src: "{{ item.path }}"
register: verification_contents
loop: "{{ verification_files.files }}"
delegate_to: localhost
run_once: true
- name: Combine verification results
set_fact:
all_verifications: "{{ all_verifications | default([]) + [item.content | b64decode | from_json] }}"
loop: "{{ verification_contents.results }}"
delegate_to: localhost
run_once: true
- name: Calculate final statistics
set_fact:
final_stats:
total_nodes: "{{ all_verifications | length }}"
auditd:
disable_requested: "{{ all_verifications | selectattr('verification_status.auditd.requested_disable', 'equalto', true) | list | length }}"
disable_successful: "{{ all_verifications | selectattr('verification_status.auditd.disable_successful', 'equalto', true) | list | length }}"
disable_failed: "{{ all_verifications | selectattr('verification_status.auditd.requested_disable', 'equalto', true) | selectattr('verification_status.auditd.disable_successful', 'equalto', false) | list | length }}"
mlocate:
disable_requested: "{{ all_verifications | selectattr('verification_status.mlocate.requested_disable', 'equalto', true) | list | length }}"
disable_successful: "{{ all_verifications | selectattr('verification_status.mlocate.disable_successful', 'equalto', true) | list | length }}"
disable_failed: "{{ all_verifications | selectattr('verification_status.mlocate.requested_disable', 'equalto', true) | selectattr('verification_status.mlocate.disable_successful', 'equalto', false) | list | length }}"
ntp:
synchronized_nodes: "{{ all_verifications | selectattr('verification_status.ntp.sync_status', 'equalto', 'synchronized') | list | length }}"
unsynchronized_nodes: "{{ all_verifications | selectattr('verification_status.ntp.sync_status', 'equalto', 'not synchronized') | list | length }}"
delegate_to: localhost
run_once: true
- name: Get failed operations details
set_fact:
failed_operations:
auditd_failed_nodes: "{{ all_verifications | selectattr('verification_status.auditd.requested_disable', 'equalto', true) | selectattr('verification_status.auditd.disable_successful', 'equalto', false) | map(attribute='hostname') | list }}"
mlocate_failed_nodes: "{{ all_verifications | selectattr('verification_status.mlocate.requested_disable', 'equalto', true) | selectattr('verification_status.mlocate.disable_successful', 'equalto', false) | map(attribute='hostname') | list }}"
time_unsync_nodes: "{{ all_verifications | selectattr('verification_status.ntp.sync_status', 'equalto', 'not synchronized') | map(attribute='hostname') | list }}"
delegate_to: localhost
run_once: true
- name: Display cluster-wide final summary
debug:
msg: |
================================================
🎯 CLUSTER-WIDE FINAL SUMMARY
================================================
Total Nodes Processed: {{ final_stats.total_nodes }}
🔍 AUDITD DISABLE OPERATIONS:
├─ Disable Requested: {{ final_stats.auditd.disable_requested }} nodes
├─ Successfully Disabled: {{ final_stats.auditd.disable_successful }} nodes
├─ Failed Disables: {{ final_stats.auditd.disable_failed }} nodes
{% if failed_operations.auditd_failed_nodes | length > 0 %}
└─ Failed Nodes: {{ failed_operations.auditd_failed_nodes | join(', ') }}
{% else %}
└─ All Operations Successful ✅
{% endif %}
📍 MLOCATE DISABLE OPERATIONS:
├─ Disable Requested: {{ final_stats.mlocate.disable_requested }} nodes
├─ Successfully Disabled: {{ final_stats.mlocate.disable_successful }} nodes
├─ Failed Disables: {{ final_stats.mlocate.disable_failed }} nodes
{% if failed_operations.mlocate_failed_nodes | length > 0 %}
└─ Failed Nodes: {{ failed_operations.mlocate_failed_nodes | join(', ') }}
{% else %}
└─ All Operations Successful ✅
{% endif %}
🕐 TIME SYNCHRONIZATION STATUS:
├─ Synchronized Nodes: {{ final_stats.ntp.synchronized_nodes }} nodes
├─ Unsynchronized Nodes: {{ final_stats.ntp.unsynchronized_nodes }} nodes
{% if failed_operations.time_unsync_nodes | length > 0 %}
└─ Needs Attention: {{ failed_operations.time_unsync_nodes | join(', ') }}
{% else %}
└─ All Nodes Synchronized ✅
{% endif %}
delegate_to: localhost
run_once: true
- name: Generate comprehensive final report CSV
copy:
content: |
Hostname,Timestamp,auditd_Disable_Requested,auditd_Current_Status,auditd_Masked,auditd_Disable_Success,mlocate_Disable_Requested,mlocate_Current_Status,mlocate_Masked,mlocate_Disable_Success,NTP_Sync_Status,Current_Time,Overall_Status
{% for verification in all_verifications -%}
{{ verification.hostname }},{{ verification.timestamp }},{{ verification.verification_status.auditd.requested_disable }},{{ verification.verification_status.auditd.current_status }},{{ verification.verification_status.auditd.is_masked }},{{ verification.verification_status.auditd.disable_successful }},{{ verification.verification_status.mlocate.requested_disable }},{{ verification.verification_status.mlocate.current_status }},{{ verification.verification_status.mlocate.is_masked }},{{ verification.verification_status.mlocate.disable_successful }},{{ verification.verification_status.ntp.sync_status }},{{ verification.verification_status.ntp.current_time }},{{ 'SUCCESS' if (not verification.verification_status.auditd.requested_disable or verification.verification_status.auditd.disable_successful) and (not verification.verification_status.mlocate.requested_disable or verification.verification_status.mlocate.disable_successful) else 'PARTIAL' }}
{% endfor %}
dest: "./final_verification_report.csv"
delegate_to: localhost
run_once: true
- name: Clean up temporary verification files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ verification_files.files }}"
delegate_to: localhost
run_once: true
# ========================================
# 권장 사후 조치
# ========================================
- name: Display post-operation recommendations
debug:
msg: |
================================================
🔧 POST-OPERATION RECOMMENDATIONS
================================================
{% if final_stats.auditd.disable_failed > 0 %}
❌ AUDITD DISABLE FAILURES:
- Manual intervention required on: {{ failed_operations.auditd_failed_nodes | join(', ') }}
- Check service dependencies: systemctl list-dependencies auditd
- Force stop if needed: systemctl stop auditd --force
- Check for active audit processes: ps aux | grep audit
{% endif %}
{% if final_stats.mlocate.disable_failed > 0 %}
❌ MLOCATE DISABLE FAILURES:
- Manual intervention required on: {{ failed_operations.mlocate_failed_nodes | join(', ') }}
- Check running updatedb processes: ps aux | grep updatedb
- Kill if necessary: pkill -f updatedb
- Retry disable: systemctl stop mlocate-updatedb.timer
{% endif %}
{% if final_stats.ntp.unsynchronized_nodes > 0 %}
⚠️ TIME SYNCHRONIZATION ISSUES:
- Nodes needing attention: {{ failed_operations.time_unsync_nodes | join(', ') }}
Manual troubleshooting steps:
1. Check NTP service status:
systemctl status chronyd
2. Verify NTP server connectivity:
chronyc sources -v
ping pool.ntp.org
3. Check firewall settings:
firewall-cmd --list-ports | grep 123
iptables -L | grep 123
4. Force time synchronization:
chrony -q 'server pool.ntp.org iburst'
5. Check system logs:
journalctl -u chronyd --since="1 hour ago"
{% endif %}
📊 MONITORING SETUP:
{% if final_stats.auditd.disable_successful > 0 %}
- auditd monitoring disabled on {{ final_stats.auditd.disable_successful }} nodes
- Alternative security monitoring recommended
{% endif %}
{% if final_stats.mlocate.disable_successful > 0 %}
- locate database updates disabled on {{ final_stats.mlocate.disable_successful }} nodes
- Manual updatedb runs required for file indexing
{% endif %}
📄 Generated Reports:
- ./final_verification_report.csv (Complete operation results)
- /var/log/ansible_service_changes.log (Change audit trail on each node)
- Time sync monitoring active on nodes with corrections
🔄 ROLLBACK PROCEDURES:
If services need to be re-enabled:
For auditd:
sudo systemctl unmask auditd
sudo systemctl enable auditd
sudo systemctl start auditd
For mlocate:
sudo systemctl unmask mlocate-updatedb.timer
sudo systemctl enable mlocate-updatedb.timer
sudo systemctl start mlocate-updatedb.timer
sudo updatedb # Rebuild database
⚡ IMMEDIATE ACTIONS REQUIRED:
{% if final_stats.auditd.disable_failed > 0 or final_stats.mlocate.disable_failed > 0 %}
- Review failed operations and resolve manually
{% endif %}
{% if final_stats.ntp.unsynchronized_nodes > 0 %}
- Fix time synchronization on unsynchronized nodes
{% endif %}
{% if final_stats.auditd.disable_successful > 0 %}
- Implement alternative security monitoring if required
{% endif %}
delegate_to: localhost
run_once: true
```j2
# Chrony configuration template
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
# Host: {{ inventory_hostname }}
# NTP Server Configuration
{% if ntp_server_pool is defined %}
pool {{ ntp_server_pool }} iburst
{% else %}
pool pool.ntp.org iburst
{% endif %}
# Additional reliable NTP servers
pool 0.pool.ntp.org iburst
pool 1.pool.ntp.org iburst
pool 2.pool.ntp.org iburst
# Local stratum 10 server for isolation
server 127.127.1.0 stratum 10
# Record the rate at which the system clock gains/losses time
driftfile /var/lib/chrony/drift
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC)
rtcsync
# Enable hardware timestamping on all interfaces that support it
#hwtimestamp *
# Increase the minimum number of selectable sources required to adjust
# the system clock
minsources 2
# Allow NTP client access from local network
{% if ansible_default_ipv4.network is defined %}
allow {{ ansible_default_ipv4.network }}/{{ ansible_default_ipv4.netmask }}
{% endif %}
allow 127.0.0.1
# Serve time even if not synchronized to a time source
local stratum 10
# Specify file containing keys for NTP authentication
keyfile /etc/chrony.keys
# Get TAI-UTC offset and leap seconds from the system tz database
leapsectz right/UTC
# Specify directory for log files
logdir /var/log/chrony
# Select which information is logged
#log measurements statistics tracking
```md
# Service Management Playbook 사용 가이드
## 개요
이 플레이북은 다중 서버 환경에서 auditd, mlocate, NTP 서비스의 상태를 체크하고 필요시 안전하게 비활성화하는 기능을 제공합니다.
## 파일 구조
.
├── service_disable_playbook.yml # 메인 플레이북
├── service_status_check_complete.yml # 서비스 상태 체크 (기존)
├── conditional_service_disable.yml # 조건부 서비스 비활성화
├── time_sync_correction.yml # 시간 동기화 교정
├── final_verification.yml # 최종 검증 및 보고
├── templates/chrony.conf.j2 # Chrony 설정 템플릿
└── README_Service_Management.md # 이 가이드
## 주요 기능
### 1. 서비스 상태 체크
- auditd 서비스 존재 여부 및 상태 확인
- mlocate/plocate 패키지 및 타이머 상태 확인
- NTP 서비스 (chronyd/ntpd/systemd-timesyncd) 상태 확인
- 시간 동기화 상태 및 노드 간 시간 차이 분석
### 2. 조건부 서비스 비활성화
- **auditd**: active 상태인 경우 안전하게 비활성화 및 마스킹
- **mlocate**: 타이머가 running 상태인 경우 비활성화 및 DB 제거 옵션
- **NTP**: 필요시 NTP 서비스 비활성화 (권장하지 않음)
### 3. 시간 동기화 교정
- 노드 간 시간 차이가 임계값을 초과하는 경우 자동 교정
- Chrony 서비스 설치 및 설정
- 시간 동기화 모니터링 설정
### 4. 안전 기능
- 설정 파일 자동 백업
- 변경 사항 로그 기록
- 순차 처리 (serial: 5)로 안정성 확보
- 서비스 마스킹으로 의도치 않은 재시작 방지
## 실행 방법
### 1. 상태 체크만 수행
```bash
ansible-playbook -i inventory service_disable_playbook.yml \
-e "disable_auditd=false" \
-e "disable_mlocate=false" \
-e "disable_ntp_services=false"
ansible-playbook -i inventory service_disable_playbook.yml \
-e "disable_auditd=true" \
-e "disable_mlocate=false" \
-e "disable_ntp_services=false"
ansible-playbook -i inventory service_disable_playbook.yml \
-e "disable_auditd=true" \
-e "disable_mlocate=true" \
-e "disable_ntp_services=false"
ansible-playbook -i inventory service_disable_playbook.yml \
-e "disable_auditd=true" \
-e "disable_mlocate=true" \
-e "max_time_drift_seconds=30" \
-e "ntp_server_pool=kr.pool.ntp.org"
ansible-playbook -i inventory service_disable_playbook.yml \
-l "webserver*" \
-e "disable_auditd=true"
| 변수명 | 기본값 | 설명 |
|---|---|---|
| disable_auditd | false | auditd 서비스 비활성화 여부 |
| disable_mlocate | false | mlocate 서비스 비활성화 여부 |
| disable_ntp_services | false | NTP 서비스 비활성화 여부 (권장하지 않음) |
| 변수명 | 기본값 | 설명 |
|---|---|---|
| max_time_drift_seconds | 30 | 시간 차이 임계값 (초) |
| ntp_server_pool | pool.ntp.org | 기본 NTP 서버 풀 |
| backup_configs | true | 설정 파일 백업 여부 |
| log_changes | true | 변경 사항 로깅 여부 |
# 시간 동기화 상태 확인
timedatectl status
# Chrony 소스 확인
chronyc sources -v
# 강제 시간 동기화
chrony -q 'server kr.pool.ntp.org iburst'
# Chrony 서비스 재시작
systemctl restart chronyd
네트워크 연결 확인
ping kr.pool.ntp.org
telnet kr.pool.ntp.org 123
방화벽 설정 확인
firewall-cmd --list-ports | grep 123
iptables -L | grep 123
하드웨어 클럭 확인
hwclock --show
hwclock --hctosys # 하드웨어 클럭을 시스템 클럭으로
전체 서비스 상태 요약 보고서
시간 동기화 상세 분석 보고서
작업 완료 후 최종 검증 보고서
/var/log/ansible_service_changes.log: 각 노드의 변경 사항 기록/var/log/time_sync_monitor.log: 시간 동기화 모니터링 로그sudo systemctl unmask auditd
sudo systemctl enable auditd
sudo systemctl start auditd
# 백업된 설정 복원 (필요시)
sudo cp /etc/audit/auditd.conf.backup.* /etc/audit/auditd.conf
sudo systemctl unmask mlocate-updatedb.timer
sudo systemctl enable mlocate-updatedb.timer
sudo systemctl start mlocate-updatedb.timer
# 데이터베이스 즉시 업데이트
sudo updatedb
locate 명령어 사용 불가, 파일 검색 속도 저하become: yes 및 sudo 권한 확인systemctl list-dependencies 로 확인ps aux | grep service_name 로 확인시스템 관리팀 또는 Ansible 관리자에게 문의