본문 바로가기
IT/Cloud

[Cloud] Ansible 클라우드 보안 자동화: 취약점 관리 및 규정 준수 가이드

by 수누다 2026. 5. 5.

클라우드 보안, 손으로 하나하나 설정하다가 사고 날 뻔했습니다

솔직히 말씀드리면, 저도 처음엔 보안 설정을 수작업으로 했어요. EC2 인스턴스 하나하나 들어가서 패키지 업데이트 확인하고, 보안 그룹 규칙 검토하고... 인스턴스가 10개쯤 됐을 때까진 그나마 버텼는데, 50개 넘어가니까 진짜 손이 두 개로는 부족하더라고요. 어느 날 감사(Audit) 리포트 보다가 패치 안 된 서버가 12대나 있다는 걸 발견했을 때 등에 식은땀이 흘렀습니다.

그때부터 Ansible 보안 자동화를 본격적으로 파기 시작했어요. 취약점 관리(Vulnerability Management)부터 규정 준수(Compliance) 검증까지, Ansible로 어떻게 자동화할 수 있는지 실무 경험을 바탕으로 풀어드릴게요. 클라우드 환경에서 보안을 체계적으로 관리하고 싶으신 분들께 실질적인 도움이 됐으면 합니다.

▲ Ansible을 중심으로 한 클라우드 보안 자동화 전체 흐름 — 인벤토리 수집부터 취약점 패치, 규정 준수 보고까지 한눈에


Ansible 보안 자동화, 이게 왜 필요한가요?

쉽게 말해서, 클라우드 환경은 서버가 수시로 생겼다 없어지거든요. 온프레미스(자체 서버실)처럼 고정된 인프라가 아니에요. 오늘 오토스케일링으로 인스턴스 10개 생겼다가 내일 5개로 줄어들 수 있잖아요. 이런 환경에서 수작업 보안 관리는 현실적으로 불가능합니다.

Ansible 보안 자동화가 필요한 이유를 정리해보면:

  • 일관성(Consistency): 모든 서버에 동일한 보안 정책 적용 — 사람이 하면 실수가 생기지만 플레이북(Playbook)은 실수 안 함
  • 속도(Speed): 수백 대 서버에 패치 적용을 몇 분 안에 완료
  • 감사 추적(Audit Trail): 언제, 어떤 변경이 있었는지 코드로 기록됨
  • 반복 가능성(Repeatability): 같은 작업을 언제든 동일하게 재현 가능
  • 드리프트 감지(Configuration Drift Detection): 설정이 원하는 상태에서 벗어나면 자동으로 교정

근데 여기서 중요한 포인트! Ansible은 에이전트리스(Agentless) 방식이에요. 관리 대상 서버에 별도 소프트웨어를 설치할 필요가 없고, SSH(또는 WinRM)만 열려 있으면 됩니다. 클라우드 환경에서 이게 엄청난 장점이거든요.


Ansible Vault로 민감 정보 보호하기

보안 자동화를 하면서 제일 먼저 부딪히는 문제가 뭔지 아세요? 바로 시크릿(Secret) 관리입니다. API 키, 데이터베이스 패스워드, 클라우드 자격증명... 이걸 플레이북 파일에 평문으로 넣으면 안 되잖아요.

Ansible에는 Ansible Vault라는 내장 암호화 도구가 있어요. 파일이나 변수를 AES-256으로 암호화해서 Git에 안전하게 올릴 수 있게 해줍니다. 제가 실제로 쓰는 방식을 보여드릴게요.

Vault 파일 생성 및 사용

# vault 암호화 파일 생성
ansible-vault create secrets/cloud_credentials.yml

# 기존 파일 암호화
ansible-vault encrypt vars/sensitive_vars.yml

# 암호화된 파일 내용 확인
ansible-vault view secrets/cloud_credentials.yml

# 암호화된 파일 수정
ansible-vault edit secrets/cloud_credentials.yml

# 플레이북 실행 시 vault 패스워드 파일 사용
ansible-playbook security_hardening.yml --vault-password-file ~/.vault_pass.txt
# secrets/cloud_credentials.yml (암호화 전 내용 예시)
---
aws_access_key: "AKIAIOSFODNN7EXAMPLE"
aws_secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
db_password: "super_secret_password_here"
api_token: "your_api_token_here"

💡 : 운영 환경에서는 vault 패스워드 파일을 직접 관리하는 것보다 HashiCorp Vault나 AWS Secrets Manager 같은 전용 시크릿 관리 서비스와 연동하는 게 훨씬 안전합니다.


취약점 관리 자동화 실전

이제 본격적으로 들어가볼게요. Ansible 취약점 관리의 핵심은 세 가지입니다: 취약 패키지 탐지 → 패치 적용 → 결과 보고. 제가 실제 환경에서 쓰는 플레이북 구조를 공유할게요.

1단계: 인벤토리 동적 구성 (Dynamic Inventory)

클라우드 환경에서는 정적 인벤토리(Static Inventory)가 아니라 동적 인벤토리(Dynamic Inventory)를 써야 해요. AWS 기준으로 보면:

# inventory/aws_ec2.yml
---
plugin: amazon.aws.aws_ec2
regions:
  - ap-northeast-2  # 서울 리전
filters:
  instance-state-name: running
  tag:Environment:
    - production
    - staging
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
hostname_source: private-ip-address
compose:
  ansible_host: private_ip_address
# 동적 인벤토리 확인
ansible-inventory -i inventory/aws_ec2.yml --list
ansible-inventory -i inventory/aws_ec2.yml --graph

2단계: 취약점 스캔 및 패치 플레이북

# playbooks/vulnerability_management.yml
---
- name: 취약점 스캔 및 보안 패치 자동화
  hosts: all
  become: yes
  gather_facts: yes
  vars_files:
    - ../secrets/cloud_credentials.yml

  vars:
    patch_reboot_required: false
    security_only: true
    report_path: "/var/log/ansible/security_report"

  tasks:
    - name: 리포트 디렉토리 생성
      file:
        path: "{{ report_path }}"
        state: directory
        mode: '0750'
        owner: root
        group: root

    - name: 패키지 캐시 업데이트 (Debian/Ubuntu)
      apt:
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"

    - name: 패키지 캐시 업데이트 (RHEL/CentOS/Amazon Linux)
      yum:
        update_cache: yes
      when: ansible_os_family == "RedHat"

    - name: 보안 업데이트 목록 확인 (Debian/Ubuntu)
      shell: apt list --upgradeable 2>/dev/null | grep -i security
      register: security_updates_deb
      changed_when: false
      failed_when: false
      when: ansible_os_family == "Debian"

    - name: 보안 업데이트 목록 확인 (RHEL 계열)
      shell: yum check-update --security 2>/dev/null | tail -n +3
      register: security_updates_rhel
      changed_when: false
      failed_when: security_updates_rhel.rc not in [0, 100]
      when: ansible_os_family == "RedHat"

    - name: 보안 패치 적용 (Debian/Ubuntu)
      apt:
        upgrade: dist
        update_cache: yes
        only_upgrade: yes
      when:
        - ansible_os_family == "Debian"
        - security_only | bool
      register: apt_upgrade_result

    - name: 보안 패치 적용 (RHEL 계열)
      yum:
        name: "*"
        state: latest
        security: yes
      when:
        - ansible_os_family == "RedHat"
        - security_only | bool
      register: yum_upgrade_result

    - name: 재부팅 필요 여부 확인 (Ubuntu)
      stat:
        path: /var/run/reboot-required
      register: reboot_required_file
      when: ansible_os_family == "Debian"

    - name: 결과 리포트 생성
      template:
        src: ../templates/security_report.j2
        dest: "{{ report_path }}/report_{{ ansible_date_time.date }}.txt"
        mode: '0640'
      vars:
        hostname: "{{ ansible_hostname }}"
        os_family: "{{ ansible_os_family }}"
        kernel_version: "{{ ansible_kernel }}"
        reboot_needed: "{{ reboot_required_file.stat.exists | default(false) }}"

    - name: 재부팅 필요 시 알림
      debug:
        msg: "⚠️ {{ ansible_hostname }} 서버는 패치 적용 후 재부팅이 필요합니다!"
      when:
        - ansible_os_family == "Debian"
        - reboot_required_file.stat.exists | default(false)

▲ Ansible 플레이북 실행 결과 — 각 호스트별 패치 적용 현황과 재부팅 필요 여부가 한눈에 표시됨

3단계: 리포트 템플릿 (Jinja2)

# templates/security_report.j2
=== 보안 패치 리포트 ===
호스트명: {{ hostname }}
OS 계열: {{ os_family }}
커널 버전: {{ kernel_version }}
실행 일시: {{ ansible_date_time.iso8601 }}
재부팅 필요: {{ reboot_needed }}

{% if ansible_os_family == 'Debian' %}
적용된 업데이트:
{{ apt_upgrade_result.stdout | default('변경 없음') }}
{% elif ansible_os_family == 'RedHat' %}
적용된 업데이트:
{{ yum_upgrade_result.results | default('변경 없음') }}
{% endif %}
========================

규정 준수 자동화 — CIS 벤치마크 적용

취약점 패치만큼 중요한 게 바로 Ansible 규정 준수 자동화예요. CIS(Center for Internet Security) 벤치마크나 NIST 프레임워크 같은 보안 기준을 서버에 자동으로 적용하고 검증하는 거죠.

저는 주로 CIS 리눅스 벤치마크를 기반으로 하드닝(보안 강화) 플레이북을 만들어서 씁니다. 핵심 항목들을 추려서 보여드릴게요.

# playbooks/cis_compliance.yml
---
- name: CIS 벤치마크 기반 보안 하드닝
  hosts: "{{ target_hosts | default('all') }}"
  become: yes
  gather_facts: yes

  vars:
    cis_level: 1  # 1 또는 2
    disable_unused_services: true
    configure_auditd: true

  tasks:
    # === 파일시스템 설정 ===
    - name: "CIS 1.1.1 - /tmp 파티션 nodev 마운트 옵션 설정"
      mount:
        name: /tmp
        src: tmpfs
        fstype: tmpfs
        opts: "defaults,rw,nosuid,nodev,noexec,relatime"
        state: mounted
      tags: [filesystem, cis_1_1]

    # === SSH 보안 설정 ===
    - name: "CIS 5.2 - SSH 보안 설정 강화"
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        state: present
        backup: yes
      loop:
        - { regexp: '^#?Protocol', line: 'Protocol 2' }
        - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
        - { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
        - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 4' }
        - { regexp: '^#?PermitEmptyPasswords', line: 'PermitEmptyPasswords no' }
        - { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
        - { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 0' }
        - { regexp: '^#?LoginGraceTime', line: 'LoginGraceTime 60' }
      notify: restart sshd
      tags: [ssh, cis_5_2]

    # === 감사 로그(Auditd) 설정 ===
    - name: "CIS 4.1 - auditd 설치 및 활성화"
      package:
        name: auditd
        state: present
      when: configure_auditd | bool
      tags: [auditd, cis_4_1]

    - name: "CIS 4.1 - auditd 규칙 설정"
      copy:
        content: |
          # 시스템 콜 감사 규칙
          -a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
          -a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change
          -w /etc/localtime -p wa -k time-change
          -w /etc/group -p wa -k identity
          -w /etc/passwd -p wa -k identity
          -w /etc/gshadow -p wa -k identity
          -w /etc/shadow -p wa -k identity
          -w /etc/sudoers -p wa -k scope
          -w /var/log/sudo.log -p wa -k actions
          -w /sbin/insmod -p x -k modules
          -w /sbin/rmmod -p x -k modules
          -w /sbin/modprobe -p x -k modules
          -e 2
        dest: /etc/audit/rules.d/cis_hardening.rules
        mode: '0640'
        owner: root
        group: root
      notify: reload auditd
      when: configure_auditd | bool
      tags: [auditd, cis_4_1]

    # === 패스워드 정책 ===
    - name: "CIS 5.4.1 - 패스워드 만료 정책 설정"
      lineinfile:
        path: /etc/login.defs
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^PASS_MAX_DAYS', line: 'PASS_MAX_DAYS   90' }
        - { regexp: '^PASS_MIN_DAYS', line: 'PASS_MIN_DAYS   7' }
        - { regexp: '^PASS_WARN_AGE', line: 'PASS_WARN_AGE   14' }
      tags: [password_policy, cis_5_4]

    # === 불필요한 서비스 비활성화 ===
    - name: "CIS 2.2 - 불필요한 서비스 비활성화"
      service:
        name: "{{ item }}"
        state: stopped
        enabled: no
      loop:
        - telnet
        - rsh
        - rlogin
        - rexec
        - tftp
        - xinetd
      failed_when: false
      when: disable_unused_services | bool
      tags: [services, cis_2_2]

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

    - name: reload auditd
      service:
        name: auditd
        state: restarted

⚠️ 삽질 경험: 이런 실수 하지 마세요

저도 처음엔 꽤 고생했거든요. 실제로 겪은 문제들을 공유할게요.

실수 1: 프로덕션에 --check 없이 바로 실행

이거 진짜 아찔했습니다. 테스트 환경에서 잘 돌아가던 플레이북을 프로덕션에 그냥 실행했다가 SSH 설정 변경으로 일부 서버 접근이 막힌 적이 있어요. 지금은 반드시 이 순서를 지킵니다:

# 1단계: 드라이런 — 실제 변경 없이 시뮬레이션
ansible-playbook cis_compliance.yml -i inventory/aws_ec2.yml --check --diff

# 2단계: 특정 태그만 먼저 테스트
ansible-playbook cis_compliance.yml -i inventory/aws_ec2.yml --tags ssh --check

# 3단계: 한 대만 먼저 적용
ansible-playbook cis_compliance.yml -i inventory/aws_ec2.yml --limit "specific_host_ip" 

# 4단계: 전체 적용
ansible-playbook cis_compliance.yml -i inventory/aws_ec2.yml

실수 2: 멱등성(Idempotency) 무시

Ansible의 핵심 철학이 멱등성(같은 작업을 여러 번 실행해도 결과가 동일)인데, 초반에 shell 모듈을 너무 남발했어요. shell로 스크립트 실행하면 매번 "changed" 상태가 되거든요. 가능하면 Ansible 내장 모듈(file, lineinfile, service 등)을 쓰는 게 맞습니다.

실수 3: Vault 패스워드 관리 소홀

팀원 여러 명이 같은 vault 패스워드를 공유하다가, 퇴사자 발생 시 전체 재암호화해야 하는 상황이 생겼어요. 지금은 CI/CD 파이프라인에서 환경변수로 관리하고, 팀원별 접근은 AWS IAM으로 통제합니다.

문제 상황 잘못된 방법 올바른 방법
민감 정보 관리 평문으로 vars 파일에 저장 Ansible Vault + Secrets Manager 연동
플레이북 테스트 프로덕션에 직접 실행 --check --diff로 드라이런 먼저
명령 실행 shell/command 모듈 남용 전용 모듈 우선 사용 (멱등성 보장)
대규모 적용 전체 호스트에 한 번에 실행 --limit으로 단계적 적용
에러 처리 failed_when 미설정 적절한 failed_when/ignore_errors 설정

규정 준수 검증 및 리포팅 자동화

보안 설정을 적용했으면, 실제로 잘 됐는지 검증하고 리포트를 뽑아야죠. 저는 Ansible과 함께 OpenSCAP(보안 설정 자동화 프로토콜 기반 도구)을 연동해서 씁니다.

# playbooks/compliance_report.yml
---
- name: 규정 준수 검증 및 HTML 리포트 생성
  hosts: all
  become: yes
  gather_facts: yes

  vars:
    report_dir: "/var/log/compliance_reports"
    scap_profile: "xccdf_org.ssgproject.content_profile_cis"

  tasks:
    - name: OpenSCAP 및 SCAP 보안 가이드 설치 (RHEL 계열)
      yum:
        name:
          - openscap-scanner
          - scap-security-guide
        state: present
      when: ansible_os_family == "RedHat"

    - name: 리포트 디렉토리 생성
      file:
        path: "{{ report_dir }}"
        state: directory
        mode: '0750'

    - name: SCAP 스캔 실행 및 HTML 리포트 생성
      command: >
        oscap xccdf eval
        --profile {{ scap_profile }}
        --results {{ report_dir }}/results_{{ ansible_hostname }}_{{ ansible_date_time.date }}.xml
        --report {{ report_dir }}/report_{{ ansible_hostname }}_{{ ansible_date_time.date }}.html
        /usr/share/xml/scap/ssg/content/ssg-rhel8-ds.xml
      register: scap_result
      failed_when: scap_result.rc not in [0, 2]
      when: ansible_os_family == "RedHat"

    - name: 스캔 결과 요약 출력
      debug:
        msg: "{{ ansible_hostname }} 스캔 완료 — 리포트: {{ report_dir }}/report_{{ ansible_hostname }}_{{ ansible_date_time.date }}.html"

    - name: 리포트 파일 로컬로 가져오기
      fetch:
        src: "{{ report_dir }}/report_{{ ansible_hostname }}_{{ ansible_date_time.date }}.html"
        dest: "./compliance_reports/"
        flat: no

▲ 규정 준수 검증 결과 대시보드 — 각 CIS 벤치마크 항목별 통과/실패/해당없음 현황을 서버별로 한눈에 파악 가능

이렇게 하면 매주 자동으로 각 서버의 규정 준수 현황을 HTML 리포트로 뽑을 수 있어요. 경영진이나 감사팀에 제출할 때도 편하고, 뭐가 문제인지 한눈에 보이거든요.


CI/CD 파이프라인과 보안 자동화 연동

사실 이게 제일 강력한 부분이에요. 깃허브 액션(GitHub Actions)이나 젠킨스(Jenkins)와 연동하면, 코드 변경이 있을 때마다 자동으로 보안 검증이 돌아가거든요.

# .github/workflows/security_automation.yml
name: 보안 자동화 파이프라인

on:
  schedule:
    - cron: '0 2 * * *'  # 매일 새벽 2시 실행
  push:
    branches: [main]
    paths:
      - 'playbooks/**'
      - 'inventory/**'

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: 코드 체크아웃
        uses: actions/checkout@v3

      - name: Python 및 Ansible 설치
        run: |
          pip install ansible ansible-lint
          ansible-galaxy collection install amazon.aws community.general

      - name: Ansible Lint 실행 (플레이북 문법/베스트프랙티스 검사)
        run: ansible-lint playbooks/

      - name: Vault 패스워드 설정
        run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > ~/.vault_pass.txt

      - name: 드라이런 실행 (실제 변경 없이 검증)
        run: |
          ansible-playbook playbooks/vulnerability_management.yml \
            -i inventory/aws_ec2.yml \
            --vault-password-file ~/.vault_pass.txt \
            --check
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: 승인 후 실제 적용 (main 브랜치만)
        if: github.ref == 'refs/heads/main'
        run: |
          ansible-playbook playbooks/vulnerability_management.yml \
            -i inventory/aws_ec2.yml \
            --vault-password-file ~/.vault_pass.txt
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

이렇게 구성하면 드리프트(원하는 상태에서 벗어남)가 발생해도 다음 날 새벽에 자동으로 교정이 되거든요. 정말 마음이 편해집니다.


정리: Ansible 클라우드 보안 자동화 핵심 체크리스트

▲ Ansible 클라우드 보안 자동화 핵심 구성 요소 요약 — Vault, 취약점 관리, 규정 준수, CI/CD 연동의 4가지 축

실무에서 느낀 건, 보안은 한 번 설정하고 끝나는 게 아니라는 거예요. 지속적으로 검증하고, 자동화해야 실수가 없습니다. Ansible 보안 자동화로 구성한 체계를 정리해보면:

  1. Ansible Vault로 모든 민감 정보 암호화 — 평문 시크릿은 절대 금지
  2. 동적 인벤토리(Dynamic Inventory)로 클라우드 환경 자동 탐지
  3. 취약점 관리 플레이북으로 정기적 보안 패치 자동화
  4. CIS 벤치마크 기반 하드닝으로 일관된 보안 기준 적용
  5. OpenSCAP 연동으로 규정 준수 검증 및 리포트 자동 생성
  6. CI/CD 파이프라인 연동으로 드리프트 자동 교정
  7. 드라이런(--check --diff)을 습관화해서 실수 방지

혹시 이 글을 보고 궁금한 점이 생기셨나요? 다음 글에서는 HashiCorp Vault와 Ansible 연동으로 더 고도화된 시크릿 관리 방법을 다뤄볼 예정이에요. Ansible 기초 인프라 자동화도 함께 보시면 이해가 더 쉬울 거예요.

뭔가 막히는 부분 있으시면 댓글로 편하게 물어보세요. 저도 삽질하면서 배운 거라, 같이 고민해드릴 수 있습니다.


자주 묻는 질문 (FAQ)

Q. Ansible로 Windows 서버 보안도 자동화할 수 있나요?

네, 가능합니다. Windows는 SSH 대신 WinRM(Windows Remote Management)을 통해 연결하고, win_updates, win_service 같은 Windows 전용 모듈을 사용합니다. 다만 설정이 리눅스보다 조금 더 복잡한 편이에요.

Q. Ansible Tower(AWX)를 써야 하나요?

소규모 환경(서버 50대 이하)이면 CLI만으로도 충분합니다. 팀 규모가 커지고, 역할 기반 접근 제어(RBAC)나 스케줄링, 웹 UI가 필요해지면 오픈소스인 AWX나 상용 버전인 Ansible Automation Platform을 고려해보세요.

Q. 플레이북 실행 중 에러가 나면 어떻게 되나요?

기본적으로 에러가 발생한 호스트에서 플레이북 실행이 중단됩니다. ignore_errors: yesfailed_when 조건을 적절히 설정해서 에러 처리를 세밀하게 제어할 수 있어요. 중요한 보안 작업은 에러 발생 시 즉시 알림을 받도록 Slack이나 이메일 핸들러를 연동하는 걸 권장합니다.