🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

보안 감사 자동화 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 10. · 16 Views

보안 감사 자동화 완벽 가이드

서버와 애플리케이션의 보안 취약점을 자동으로 점검하고, 90점 이상의 보안 수준을 유지하는 방법을 배웁니다. Lynis와 OpenSCAP을 활용한 실전 보안 자동화 전략을 소개합니다.


목차

  1. Lynis 설치와 기본 스캔
  2. OpenSCAP 프로파일과 정책
  3. 취약점 스캔 결과 JSON 변환
  4. GitHub Actions로 자동 스캔
  5. 90점 이상 유지 정책 수립
  6. 패치 결과 MR 필수 워크플로우

1. Lynis 설치와 기본 스캔

어느 날 김개발 씨는 운영 중인 서버가 해킹당했다는 소식을 듣고 깜짝 놀랐습니다. "우리 서버는 괜찮겠지?"라는 불안감이 엄습했습니다.

박시니어 씨가 말했습니다. "매일 수동으로 보안 점검하기엔 너무 번거로워요.

자동화가 필요합니다."

Lynis는 Unix 계열 시스템의 보안 상태를 종합적으로 점검하는 오픈소스 도구입니다. 마치 건강검진처럼 시스템의 보안 건강도를 측정합니다.

방화벽 설정부터 파일 권한, 패키지 업데이트 상태까지 수백 가지 항목을 자동으로 검사합니다.

다음 코드를 살펴봅시다.

# Lynis 설치 및 기본 스캔
sudo apt-get update
sudo apt-get install lynis -y

# 전체 시스템 보안 감사 실행
sudo lynis audit system

# JSON 형식으로 결과 저장
sudo lynis audit system --report-file /tmp/lynis-report.dat

# 점수 확인 (Hardening index)
grep "hardening_index" /var/log/lynis-report.dat

김개발 씨는 입사 6개월 차 백엔드 개발자입니다. 최근 회사에서 보안 사고가 발생했다는 소식을 듣고 불안해졌습니다.

"우리 팀이 관리하는 서버들은 안전할까?" 밤잠을 설치며 고민하던 그는 다음 날 아침 박시니어 씨를 찾아갔습니다. "시니어님, 서버 보안 점검을 어떻게 하면 좋을까요?" 김개발 씨의 질문에 박시니어 씨가 미소를 지으며 답했습니다.

"좋은 질문이네요. 수동으로 하나하나 확인하기엔 너무 시간이 오래 걸려요.

Lynis라는 도구를 사용하면 자동으로 점검할 수 있습니다." Lynis는 무엇일까요? 쉽게 비유하자면, Lynis는 시스템의 건강검진 도구입니다. 병원에서 혈압, 혈당, 콜레스테롤 등을 종합적으로 검사하듯이, Lynis는 서버의 방화벽 설정, 파일 권한, 네트워크 설정, 패키지 업데이트 상태 등을 한 번에 점검합니다.

검사가 끝나면 0점부터 100점까지 점수를 매겨줍니다. 보안 점검이 없던 시절의 문제 과거에는 서버 보안을 점검하려면 시스템 관리자가 직접 체크리스트를 만들고 하나씩 확인해야 했습니다.

방화벽 규칙이 제대로 설정되어 있는지, 불필요한 서비스가 실행 중인지, 파일 권한이 올바른지 등 수백 가지 항목을 일일이 확인하는 것은 정말 지루하고 시간이 많이 걸리는 작업이었습니다. 더 큰 문제는 사람이 직접 확인하다 보면 실수가 생긴다는 점이었습니다.

중요한 항목을 빠뜨리거나, 점검 기준이 사람마다 달라지는 일이 빈번했습니다. 또한 서버가 10대, 20대로 늘어나면 모든 서버를 점검하는 것 자체가 불가능에 가까웠습니다.

Lynis의 등장 바로 이런 문제를 해결하기 위해 Lynis가 등장했습니다. Lynis를 사용하면 단 한 줄의 명령어로 시스템 전체를 점검할 수 있습니다.

수백 가지 보안 항목을 자동으로 검사하고, 발견된 문제점과 개선 방안을 상세하게 알려줍니다. 무엇보다 일관된 기준으로 점검하기 때문에 사람의 실수를 줄일 수 있습니다.

설치 과정 살펴보기 위의 코드를 살펴보겠습니다. 첫 번째 줄에서 apt-get update로 패키지 목록을 최신화합니다.

이는 최신 버전의 Lynis를 설치하기 위한 준비 작업입니다. 다음으로 apt-get install lynis 명령으로 Lynis를 설치합니다.

-y 옵션은 설치 확인 질문에 자동으로 yes를 답하게 만듭니다. 세 번째 명령인 lynis audit system이 핵심입니다.

이 명령을 실행하면 Lynis가 시스템 전체를 스캔하기 시작합니다. 스캔에는 보통 2-3분 정도 걸리며, 진행 상황이 실시간으로 화면에 표시됩니다.

스캔 결과는 어떻게 확인할까? 스캔이 완료되면 화면에 다양한 정보가 출력됩니다. 하지만 나중에 다시 확인하려면 결과를 파일로 저장하는 것이 좋습니다.

--report-file 옵션을 사용하면 스캔 결과를 지정한 경로에 저장할 수 있습니다. 마지막으로 grep 명령으로 hardening_index를 찾아보면 시스템의 보안 점수를 확인할 수 있습니다.

이 점수는 0부터 100까지이며, 70점 이상이면 양호한 상태, 90점 이상이면 매우 안전한 상태로 볼 수 있습니다. 실무에서의 활용 실제 현업에서는 어떻게 활용할까요?

예를 들어 전자상거래 서비스를 운영하는 회사라면 고객의 개인정보와 결제 정보를 다루기 때문에 보안이 매우 중요합니다. 매주 월요일 아침마다 Lynis 스캔을 실행하여 보안 점수를 확인하고, 점수가 떨어지면 즉시 원인을 파악하여 조치하는 방식으로 활용할 수 있습니다.

주의할 점 초보자들이 흔히 하는 실수가 있습니다. Lynis는 반드시 sudo 권한으로 실행해야 합니다.

일반 사용자 권한으로 실행하면 많은 시스템 파일에 접근할 수 없어 정확한 결과를 얻을 수 없습니다. 또한 스캔 중에는 시스템 리소스를 상당히 사용하므로, 서비스 트래픽이 적은 시간대에 실행하는 것이 좋습니다.

첫 걸음을 내딛다 박시니어 씨의 설명을 들은 김개발 씨는 곧바로 개발 서버에서 Lynis를 실행해 보았습니다. "와, 정말 간단하네요!" 스캔 결과를 보니 68점이 나왔습니다.

생각보다 낮은 점수에 놀랐지만, 개선할 부분이 명확하게 보였습니다. Lynis를 제대로 활용하면 서버의 보안 상태를 객관적으로 파악하고 지속적으로 개선할 수 있습니다.

여러분도 오늘 배운 내용을 실제 서버에 적용해 보세요.

실전 팁

💡 - 최초 스캔 결과는 백업해두고 개선 전후를 비교하세요

  • 주간 단위로 정기 스캔을 실행하여 보안 추세를 모니터링하세요
  • 점수보다 중요한 것은 발견된 취약점을 실제로 조치하는 것입니다

2. OpenSCAP 프로파일과 정책

김개발 씨가 Lynis로 보안 점검을 하던 중 박시니어 씨가 말했습니다. "Lynis도 좋지만, 정부 기관이나 금융권에서 요구하는 보안 규정을 준수하려면 OpenSCAP도 알아야 해요." 김개발 씨는 고개를 갸우뚱했습니다.

"둘이 뭐가 다른가요?"

OpenSCAP는 보안 정책 준수 여부를 검사하는 프레임워크입니다. 마치 법규를 준수하는지 감사하는 감사관과 같습니다.

CIS, NIST, PCI-DSS 같은 산업 표준 보안 정책을 자동으로 검사하고, 규정 준수율을 리포트로 제공합니다.

다음 코드를 살펴봅시다.

# OpenSCAP 설치
sudo apt-get install libopenscap8 -y

# Ubuntu용 보안 가이드 다운로드
wget https://security.ubuntu.com/ubuntu/com.ubuntu.$(lsb_release -cs).usn.oval.xml

# CIS 벤치마크 프로파일로 스캔 실행
sudo oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --results /tmp/oscap-results.xml \
  --report /tmp/oscap-report.html \
  /usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml

# HTML 리포트 확인
firefox /tmp/oscap-report.html

김개발 씨가 Lynis로 첫 번째 보안 스캔을 마치고 뿌듯해하던 그날 오후, 박시니어 씨가 그의 자리로 다가왔습니다. "김개발 씨, 잘하고 있네요.

그런데 우리 회사가 금융권 클라이언트와 계약하려면 PCI-DSS 인증이 필요한데, Lynis만으로는 부족해요." 김개발 씨는 처음 듣는 용어에 당황했습니다. "PCI-DSS요?

그게 뭔가요?" 박시니어 씨가 화이트보드에 설명을 시작했습니다. "보안 규정에는 여러 종류가 있어요.

이걸 자동으로 검사하는 도구가 바로 OpenSCAP입니다." OpenSCAP는 무엇이 다를까? 쉽게 비유하자면, Lynis는 건강검진 센터이고 OpenSCAP는 법규 감사 기관입니다. 건강검진은 몸 상태를 전반적으로 확인하지만, 법규 감사는 특정 법령을 제대로 지키고 있는지 체크합니다.

OpenSCAP는 CIS 벤치마크, NIST 가이드라인, PCI-DSS 같은 산업 표준 보안 정책을 얼마나 잘 준수하고 있는지 점검합니다. 예를 들어 CIS 벤치마크는 전 세계 보안 전문가들이 합의한 보안 설정 기준입니다.

"SSH 포트는 기본 22번 대신 다른 번호를 사용해야 한다", "root 계정으로 직접 로그인할 수 없어야 한다" 같은 수백 가지 규칙이 정의되어 있습니다. 보안 규정 준수가 왜 중요할까? 과거에는 각 회사가 자체적으로 보안 기준을 만들어 사용했습니다.

하지만 이렇게 하면 회사마다 보안 수준이 천차만별이 되고, 협력 업체나 고객 입장에서는 그 회사가 안전한지 판단하기 어려웠습니다. 금융권이나 의료 분야처럼 민감한 데이터를 다루는 산업에서는 이런 문제가 특히 심각했습니다.

그래서 업계 표준 보안 규정이 등장했고, 이를 준수하지 않으면 사업 자체가 불가능해졌습니다. OpenSCAP의 강력한 기능 OpenSCAP를 사용하면 선택한 보안 정책에 따라 시스템을 자동으로 검사할 수 있습니다.

예를 들어 CIS 벤치마크 레벨 1 프로파일을 선택하면, 해당 프로파일에 정의된 모든 규칙을 자동으로 검사합니다. 각 규칙마다 통과(pass), 실패(fail), 해당없음(notapplicable) 결과가 나오고, 전체 준수율이 퍼센트로 표시됩니다.

코드 한 줄씩 이해하기 위의 코드를 자세히 살펴보겠습니다. 첫 번째 명령은 OpenSCAP 라이브러리를 설치합니다.

두 번째 명령은 Ubuntu 보안 가이드 파일을 다운로드합니다. 이 파일에는 Ubuntu 시스템에 적용할 수 있는 보안 규칙들이 XML 형식으로 정의되어 있습니다.

핵심은 세 번째 명령인 oscap xccdf eval입니다. --profile 옵션으로 어떤 보안 정책을 적용할지 선택합니다.

여기서는 CIS 벤치마크 프로파일을 선택했습니다. --results 옵션은 상세한 결과를 XML 파일로 저장하고, --report 옵션은 사람이 읽기 쉬운 HTML 리포트를 생성합니다.

마지막 줄은 생성된 HTML 리포트를 웹 브라우저로 엽니다. 리포트를 열어보면 어떤 규칙을 통과했고 어떤 규칙을 실패했는지 상세하게 나옵니다.

리포트 읽는 방법 HTML 리포트는 매우 직관적입니다. 상단에는 전체 점수와 통과율이 표시됩니다.

예를 들어 "85/120 규칙 통과 (70.8%)"처럼 나옵니다. 아래로 스크롤하면 각 규칙의 상세 결과를 볼 수 있습니다.

실패한 규칙을 클릭하면 왜 실패했는지, 어떻게 수정해야 하는지 설명이 나옵니다. 예를 들어 "SSH 설정 파일에서 PermitRootLogin이 yes로 설정되어 있습니다.

no로 변경하세요"처럼 구체적인 조치 방법이 제시됩니다. 실무 시나리오 실제로 금융 서비스를 개발하는 회사를 가정해봅시다.

고객사로부터 "PCI-DSS 준수 인증서를 제출해주세요"라는 요청을 받았습니다. 이때 OpenSCAP로 PCI-DSS 프로파일을 스캔하여 준수율을 확인합니다.

만약 95% 이상이면 나머지 5%의 미흡한 부분을 개선하고 재스캔합니다. 100%를 달성하면 스캔 리포트를 인증 자료로 제출할 수 있습니다.

흔한 실수 피하기 초보자들이 자주 실수하는 부분이 있습니다. 첫째, 프로파일 경로를 잘못 입력하는 경우입니다.

Ubuntu 버전마다 경로가 다르므로 정확히 확인해야 합니다. 둘째, 스캔 시간을 과소평가하는 것입니다.

수백 가지 규칙을 검사하므로 5-10분 정도 걸릴 수 있습니다. 셋째, 리포트의 경고를 무시하는 것입니다.

통과한 규칙도 중요하지만, 실패한 규칙을 반드시 조치해야 합니다. 새로운 도구 마스터하기 김개발 씨는 OpenSCAP를 처음 실행해보고 놀랐습니다.

리포트를 열어보니 120개 규칙 중 68개만 통과했습니다. "이렇게 많이 부족했다니..." 하지만 각 규칙마다 개선 방법이 친절하게 설명되어 있어서 하나씩 고쳐나갈 수 있었습니다.

산업 표준 보안 규정을 준수하는 것은 선택이 아닌 필수입니다. OpenSCAP를 활용하면 복잡한 규정도 자동으로 검사하고 개선할 수 있습니다.

실전 팁

💡 - 여러 프로파일 중 자신의 산업군에 맞는 것을 선택하세요

  • 리포트는 반드시 백업하여 개선 이력을 추적하세요
  • 100% 준수가 어렵다면 우선순위가 높은 규칙부터 조치하세요

3. 취약점 스캔 결과 JSON 변환

김개발 씨가 Lynis와 OpenSCAP 리포트를 보며 고민에 빠졌습니다. "이 결과를 어떻게 팀원들과 공유하고, 추이를 추적하지?" 박시니어 씨가 말했습니다.

"리포트를 JSON으로 변환하면 프로그램으로 처리할 수 있어요. 그래프도 그리고 데이터베이스에도 저장할 수 있죠."

보안 스캔 결과를 JSON 형식으로 변환하면 프로그래밍으로 자유롭게 가공할 수 있습니다. 마치 요리 재료를 손질하여 다양한 요리를 만드는 것처럼, JSON 데이터를 파싱하여 차트, 대시보드, 알림 시스템 등에 활용할 수 있습니다.

다음 코드를 살펴봅시다.

import json
import subprocess
from datetime import datetime

def parse_lynis_report():
    # Lynis 스캔 실행 및 결과 파싱
    result = subprocess.run(['sudo', 'lynis', 'audit', 'system', '--quiet'],
                          capture_output=True, text=True)

    # 보고서 파일 읽기
    with open('/var/log/lynis-report.dat', 'r') as f:
        lines = f.readlines()

    # 핵심 지표 추출
    report = {
        'timestamp': datetime.now().isoformat(),
        'hardening_index': 0,
        'warnings': [],
        'suggestions': []
    }

    for line in lines:
        if 'hardening_index=' in line:
            report['hardening_index'] = int(line.split('=')[1])
        elif line.startswith('warning[]='):
            report['warnings'].append(line.split('=')[1].strip())
        elif line.startswith('suggestion[]='):
            report['suggestions'].append(line.split('=')[1].strip())

    # JSON 파일로 저장
    with open('/tmp/security-report.json', 'w') as f:
        json.dump(report, f, indent=2)

    return report

# 실행 예시
report = parse_lynis_report()
print(f"보안 점수: {report['hardening_index']}")
print(f"경고 개수: {len(report['warnings'])}")

김개발 씨는 지난 일주일 동안 매일 보안 스캔을 실행했습니다. 하지만 문제가 생겼습니다.

스캔할 때마다 터미널에 수백 줄의 텍스트가 출력되는데, 이전 스캔 결과와 비교하기가 너무 어려웠습니다. "지난주에 비해 보안이 개선되었는지 어떻게 알지?" 박시니어 씨가 김개발 씨의 화면을 보고 고개를 끄덕였습니다.

"텍스트 리포트는 사람이 읽기엔 좋지만 프로그램으로 처리하기 어렵죠. JSON 형식으로 바꿔보세요." JSON이 왜 필요할까? 쉽게 비유하자면, 텍스트 리포트는 종이 영수증이고 JSON은 디지털 영수증입니다.

종이 영수증은 눈으로 보기엔 편하지만, 가계부 앱에 자동으로 입력하거나 월별 통계를 내려면 일일이 손으로 입력해야 합니다. 반면 디지털 영수증은 자동으로 앱에 저장되고, 차트도 그려지고, 예산 초과 시 알림도 받을 수 있습니다.

보안 스캔 결과도 마찬가지입니다. JSON 형식으로 저장하면 프로그래밍 언어로 쉽게 읽고 가공할 수 있습니다.

보안 점수 추이 그래프를 그리거나, 경고가 5개 이상이면 슬랙에 알림을 보내거나, 데이터베이스에 저장하여 이력을 관리할 수 있습니다. 수동 처리의 한계 JSON 변환 없이 텍스트 리포트만 사용하던 시절의 문제점을 생각해봅시다.

매일 스캔을 실행하면 리포트 파일이 쌓이는데, 이를 일일이 열어서 점수를 비교하려면 엄청난 시간이 듭니다. 또한 여러 서버를 관리한다면 각 서버의 리포트를 따로 확인해야 하므로 더욱 번거롭습니다.

중요한 경고를 놓치는 일도 빈번했습니다. 자동화의 시작, JSON 파싱 JSON으로 변환하면 이 모든 문제가 해결됩니다.

Python이나 JavaScript 같은 언어로 JSON 파일을 읽어서 원하는 데이터만 추출할 수 있습니다. 보안 점수만 따로 모아서 꺾은선 그래프를 그리거나, 경고 개수를 집계하여 가장 자주 발생하는 문제를 파악할 수 있습니다.

코드 상세 분석 위의 Python 코드를 단계별로 살펴보겠습니다. 먼저 subprocess 모듈로 Lynis 명령을 실행합니다.

--quiet 옵션을 사용하면 불필요한 출력을 줄일 수 있습니다. 스캔이 완료되면 결과는 /var/log/lynis-report.dat 파일에 저장됩니다.

다음으로 이 파일을 열어서 한 줄씩 읽습니다. 각 줄을 검사하여 hardening_index, warning, suggestion 같은 핵심 정보를 찾아냅니다.

예를 들어 "hardening_index=68"이라는 줄을 발견하면 =을 기준으로 분리하여 숫자 68을 추출합니다. 추출한 데이터는 Python 딕셔너리에 담습니다.

딕셔너리는 JSON과 구조가 같기 때문에 json.dump() 함수로 쉽게 JSON 파일로 저장할 수 있습니다. indent=2 옵션을 주면 사람이 읽기 쉽게 들여쓰기가 적용됩니다.

생성된 JSON 구조 저장된 JSON 파일을 열어보면 다음과 같은 구조입니다. timestamp 필드에는 스캔한 시각이 ISO 형식으로 기록됩니다.

hardening_index에는 보안 점수가 숫자로 저장됩니다. warnings 배열에는 발견된 모든 경고가 리스트로 담깁니다.

suggestions 배열에는 개선 제안 사항이 저장됩니다. 이 JSON 데이터를 읽어서 원하는 방식으로 가공할 수 있습니다.

예를 들어 pandas 라이브러리로 DataFrame을 만들어 엑셀 차트처럼 분석할 수도 있고, matplotlib로 시각화할 수도 있습니다. 실무 활용 사례 실제 DevOps 팀에서는 이렇게 활용합니다.

매일 새벽 3시에 크론잡으로 보안 스캔을 실행하고 결과를 JSON으로 저장합니다. 그리고 Python 스크립트가 이 JSON을 읽어서 MongoDB에 저장합니다.

웹 대시보드에서는 최근 30일간의 보안 점수 추이를 그래프로 보여줍니다. 점수가 전날보다 10점 이상 떨어지면 슬랙 채널에 자동으로 알림이 전송됩니다.

주의사항 초보자들이 주의해야 할 점이 있습니다. 첫째, 파일 권한 문제입니다.

/var/log 디렉토리는 root 권한이 필요하므로 스크립트도 sudo로 실행해야 합니다. 둘째, 파싱 로직을 견고하게 만들어야 합니다.

Lynis 버전이 업데이트되면 리포트 형식이 약간 바뀔 수 있으므로 에러 처리를 추가하세요. 셋째, JSON 파일이 계속 쌓이면 디스크 공간을 차지하므로 오래된 파일은 주기적으로 삭제하거나 압축 보관하세요.

데이터 활용의 무한한 가능성 김개발 씨는 JSON 변환 스크립트를 작성한 후 신세계를 경험했습니다. "이제 보안 점수를 그래프로 볼 수 있네!" 한 달간의 데이터를 모아 차트를 그려보니, 패치를 적용한 날 점수가 급상승한 것이 한눈에 보였습니다.

보안 데이터를 JSON으로 관리하면 단순한 점검을 넘어 전략적인 보안 관리가 가능해집니다. 여러분도 스캔 결과를 JSON으로 변환하여 자동화의 다음 단계로 나아가세요.

실전 팁

💡 - JSON 스키마를 문서화하여 팀원들과 공유하세요

  • 데이터베이스에 저장할 때는 타임스탬프를 인덱스로 설정하세요
  • 중요한 필드만 추출하여 JSON 크기를 최소화하세요

4. GitHub Actions로 자동 스캔

김개발 씨가 수동으로 보안 스캔을 실행하던 어느 날, 깜빡하고 스캔을 건너뛴 적이 있었습니다. "아차, 오늘 스캔 안 했네..." 박시니어 씨가 웃으며 말했습니다.

"사람은 실수하기 마련이죠. GitHub Actions로 자동화하면 절대 잊어버리지 않아요."

GitHub Actions는 코드 저장소에 이벤트가 발생할 때 자동으로 작업을 실행하는 CI/CD 도구입니다. 마치 알람 시계가 정해진 시각에 자동으로 울리듯이, 매일 정해진 시간에 보안 스캔을 자동으로 실행하고 결과를 저장할 수 있습니다.

다음 코드를 살펴봅시다.

# .github/workflows/security-audit.yml
name: Security Audit

on:
  schedule:
    - cron: '0 2 * * *'  # 매일 새벽 2시에 실행
  workflow_dispatch:  # 수동 실행도 가능

jobs:
  security-scan:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Install Lynis
        run: |
          sudo apt-get update
          sudo apt-get install -y lynis

      - name: Run security audit
        run: |
          sudo lynis audit system --quiet
          sudo cat /var/log/lynis-report.dat > lynis-report.txt

      - name: Parse and upload results
        run: |
          python scripts/parse_security_report.py

      - name: Upload report artifact
        uses: actions/upload-artifact@v3
        with:
          name: security-report
          path: security-report.json

      - name: Notify on Slack
        if: failure()
        run: |
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
            -H 'Content-Type: application/json' \
            -d '{"text":"보안 스캔 실패!"}'

김개발 씨는 지난 한 달 동안 성실하게 매일 아침 출근하자마자 보안 스캔을 실행했습니다. 하지만 어느 월요일, 주말 동안 긴급 배포가 있었고 피곤한 상태로 출근한 그는 스캔하는 것을 깜빡했습니다.

"아, 오늘 스캔 안 했네! 벌써 오후 4시인데..." 박시니어 씨가 지나가다 김개발 씨의 당황한 모습을 보고 말했습니다.

"매일 사람이 직접 실행하는 건 무리예요. 자동화하면 훨씬 편하죠.

GitHub Actions 써보셨어요?" GitHub Actions가 뭘까? 쉽게 비유하자면, GitHub Actions는 24시간 일하는 로봇 비서입니다. "매일 새벽 2시에 보안 스캔 실행해줘", "코드가 푸시되면 테스트 돌려줘", "배포할 때 알림 보내줘" 같은 명령을 내리면 정확하게 수행합니다.

사람처럼 피곤하거나 깜빡하지 않고, 365일 쉬지 않고 일합니다. GitHub Actions는 워크플로우라는 설정 파일로 동작합니다.

YAML 형식으로 작성하며, 언제 실행할지, 무슨 작업을 할지 상세히 정의합니다. 수동 실행의 한계 매일 사람이 직접 보안 스캔을 실행하는 방식의 문제점을 생각해봅시다.

첫째, 일정이 바쁘거나 출장 중이면 잊어버리기 쉽습니다. 둘째, 팀원마다 스캔하는 시간이 달라서 결과를 비교하기 어렵습니다.

셋째, 누군가는 스캔 후 결과를 저장하지 않고 넘어갈 수 있습니다. 넷째, 주말에는 아무도 스캔하지 않아 금요일 배포 후 문제가 생겨도 월요일까지 모를 수 있습니다.

자동화의 위력 GitHub Actions로 자동화하면 이런 문제가 모두 사라집니다. 크론 스케줄을 설정하면 지정한 시간에 정확하게 실행됩니다.

예를 들어 매일 새벽 2시로 설정하면, 서비스 트래픽이 적은 시간대에 자동으로 스캔이 돌아갑니다. 결과는 자동으로 저장되고, 문제가 발견되면 슬랙이나 이메일로 알림이 갑니다.

워크플로우 파일 뜯어보기 위의 YAML 코드를 한 줄씩 살펴보겠습니다. 첫 번째 줄의 name은 워크플로우의 이름입니다.

GitHub Actions 탭에서 이 이름으로 표시됩니다. on 섹션은 언제 실행할지 정의합니다.

schedule의 cron 표현식 '0 2 * * *'는 "매일 새벽 2시 0분"을 의미합니다. workflow_dispatch는 필요할 때 수동으로 실행할 수 있게 해줍니다.

jobs 섹션은 실제로 할 일을 정의합니다. security-scan이라는 작업은 ubuntu-latest 환경에서 실행됩니다.

steps는 순차적으로 실행될 단계들입니다. 첫 번째 step은 코드를 체크아웃합니다.

이는 GitHub 저장소의 코드를 가져오는 작업입니다. 두 번째 step은 Lynis를 설치합니다.

세 번째 step은 실제 보안 스캔을 실행하고 결과를 텍스트 파일로 저장합니다. 네 번째 step은 Python 스크립트를 실행하여 결과를 JSON으로 변환합니다.

다섯 번째 step은 생성된 JSON 파일을 아티팩트로 업로드합니다. 아티팩트는 GitHub Actions에서 생성된 파일을 저장하는 기능으로, 나중에 다운로드할 수 있습니다.

조건부 실행과 알림 마지막 step을 주목해봅시다. if: failure() 조건은 앞선 단계 중 하나라도 실패하면 이 step을 실행한다는 의미입니다.

보안 스캔이 실패하거나 점수가 급격히 떨어지면, 미리 설정해둔 슬랙 웹훅 URL로 POST 요청을 보내 알림을 전송합니다. 슬랙 채널에 "보안 스캔 실패!"라는 메시지가 즉시 나타납니다.

secrets.SLACK_WEBHOOK은 GitHub 저장소의 시크릿에 저장된 값입니다. 중요한 정보를 코드에 직접 쓰지 않고 안전하게 관리할 수 있습니다.

실무에서의 활용 실제 DevOps 팀은 이렇게 운영합니다. 메인 브랜치에 코드가 병합될 때마다 자동으로 보안 스캔을 실행하는 워크플로우를 추가합니다.

또한 매일 새벽 2시에 정기 스캔을 실행하는 워크플로우도 별도로 만듭니다. 두 가지를 함께 사용하면 코드 변경 시점과 정기 점검 시점 모두 보안 상태를 확인할 수 있습니다.

스캔 결과는 GitHub의 아티팩트로 저장되어 30일간 보관됩니다. 팀원 누구나 Actions 탭에서 과거 스캔 결과를 다운로드할 수 있습니다.

흔한 실수 피하기 초보자들이 자주 하는 실수가 있습니다. 첫째, 크론 표현식을 잘못 쓰는 것입니다.

GitHub Actions의 크론은 UTC 기준이므로 한국 시간으로 새벽 2시라면 '0 17 * * *'로 써야 합니다 (UTC + 9시간). 둘째, 시크릿을 설정하지 않아 알림이 작동하지 않는 경우입니다.

반드시 저장소 설정에서 SLACK_WEBHOOK을 등록하세요. 셋째, sudo 권한 문제로 Lynis가 제대로 실행되지 않는 경우입니다.

GitHub Actions의 ubuntu 환경에서는 sudo를 사용할 수 있습니다. 자동화의 완성 김개발 씨는 GitHub Actions 워크플로우를 설정한 후 편안한 마음으로 주말을 보낼 수 있었습니다.

"이제 내가 잊어버려도 자동으로 돌아가니까 안심이야." 월요일 아침 출근하자 슬랙에 "일요일 보안 스캔 완료, 점수 85점" 알림이 와 있었습니다. 보안 점검을 자동화하면 사람의 실수를 없애고 일관된 모니터링을 유지할 수 있습니다.

여러분도 GitHub Actions로 보안 자동화를 시작해보세요.

실전 팁

💡 - 크론 스케줄은 UTC 기준이므로 시간대 변환에 주의하세요

  • 아티팩트 보관 기간은 저장소 설정에서 조정할 수 있습니다
  • 워크플로우를 테스트할 때는 workflow_dispatch로 수동 실행하세요

5. 90점 이상 유지 정책 수립

김개발 씨는 자동화된 보안 스캔을 운영하며 한 가지 의문이 들었습니다. "스캔은 자동으로 돌아가는데, 점수가 떨어지면 어떻게 하지?

기준이 필요한데..." 박시니어 씨가 고개를 끄덕이며 말했습니다. "바로 그거예요.

정책이 필요합니다. 우리 팀은 항상 90점 이상을 유지하기로 했어요."

보안 정책은 보안 점수의 최소 기준을 정하고, 기준 미달 시 자동으로 경고하거나 배포를 차단하는 규칙입니다. 마치 학교의 진급 기준처럼, 90점 이상이어야 프로덕션 배포를 허용하는 식으로 강제할 수 있습니다.

다음 코드를 살펴봅시다.

# scripts/check_security_threshold.py
import json
import sys

def check_security_threshold():
    # JSON 리포트 읽기
    with open('security-report.json', 'r') as f:
        report = json.load(f)

    # 기준 점수 설정
    MINIMUM_SCORE = 90
    WARNING_SCORE = 85

    hardening_index = report['hardening_index']
    warnings_count = len(report['warnings'])

    print(f"현재 보안 점수: {hardening_index}")
    print(f"경고 개수: {warnings_count}")

    # 정책 검증
    if hardening_index < MINIMUM_SCORE:
        print(f"❌ 실패: 보안 점수가 최소 기준({MINIMUM_SCORE}점) 미달입니다.")
        print("배포가 차단되었습니다. 취약점을 먼저 해결하세요.")
        sys.exit(1)  # 실패 코드로 종료

    elif hardening_index < WARNING_SCORE:
        print(f"⚠️  경고: 보안 점수가 낮습니다 ({WARNING_SCORE}점 이하).")
        print("가능한 빨리 개선하세요.")

    else:
        print(f"✅ 통과: 보안 기준을 충족합니다.")

    # 치명적 경고가 있는지 확인
    critical_warnings = [w for w in report['warnings']
                        if 'root' in w.lower() or 'password' in w.lower()]

    if critical_warnings:
        print(f"❌ 치명적 경고 발견: {len(critical_warnings)}건")
        for warning in critical_warnings[:3]:  # 상위 3개만 출력
            print(f"  - {warning}")
        sys.exit(1)

    return hardening_index

if __name__ == '__main__':
    check_security_threshold()

김개발 씨의 팀은 지난 두 달 동안 자동 보안 스캔을 운영했습니다. 점수는 대체로 70~85점 사이를 오갔습니다.

그런데 어느 날 개발자 한 명이 서둘러 배포한 후, 보안 점수가 55점으로 급락했습니다. 다행히 큰 문제는 없었지만, 박시니어 씨는 위기를 느꼈습니다.

"이런 식으로는 안 돼요. 점수가 아무리 낮아도 배포가 되면 위험하죠." 박시니어 씨는 팀 회의를 소집했습니다.

"앞으로는 보안 점수 90점 이상이 아니면 배포를 못 하도록 막겠습니다." 보안 정책이란? 쉽게 비유하자면, 보안 정책은 건물 출입 통제 시스템과 같습니다. 신분증이 없으면 건물에 들어갈 수 없듯이, 보안 점수가 기준 미달이면 프로덕션 환경에 배포할 수 없게 만듭니다.

이는 강제성을 부여하여 개발자들이 보안을 소홀히 하지 못하게 합니다. 정책의 핵심은 명확한 기준입니다.

"최소 90점", "경고 0건", "치명적 취약점 없음" 같은 구체적인 숫자와 조건을 정해야 합니다. 정책 없이는 위험하다 보안 정책 없이 운영하던 시절의 문제를 생각해봅시다.

보안 스캔을 실행해도 결과가 단순 참고용이었습니다. 점수가 낮아도 "나중에 고치지 뭐"라며 배포가 진행되었습니다.

그러다 보면 취약점이 누적되고, 어느 순간 보안 사고로 이어집니다. 사고가 터진 후에는 "왜 미리 막지 않았냐"는 질책만 남습니다.

강제력 있는 정책 보안 정책을 수립하면 시스템이 자동으로 강제합니다. GitHub Actions 워크플로우에서 보안 점검 스크립트를 실행하고, 스크립트가 실패 코드(exit 1)를 반환하면 워크플로우 전체가 실패합니다.

배포 단계는 보안 점검이 통과해야만 실행되도록 설정하면, 점수가 낮은 상태로는 절대 배포할 수 없습니다. 코드 동작 원리 위의 Python 코드를 자세히 분석해봅시다.

먼저 security-report.json 파일을 읽어서 보안 점수와 경고 목록을 가져옵니다. MINIMUM_SCORE 변수에 최소 허용 점수 90을 설정하고, WARNING_SCORE에 경고 기준 85를 설정합니다.

다음으로 현재 보안 점수를 출력합니다. 그리고 조건문으로 점수를 검증합니다.

만약 90점 미만이면 "실패" 메시지를 출력하고 sys.exit(1)로 프로그램을 종료합니다. 종료 코드 1은 실패를 의미하며, GitHub Actions는 이를 감지하여 워크플로우를 중단합니다.

85점 이상 90점 미만이면 경고 메시지만 출력하고 종료 코드 0으로 통과시킵니다. 이는 당장 차단하지는 않지만 주의가 필요한 상태입니다.

90점 이상이면 "통과" 메시지를 출력합니다. 치명적 경고 별도 검사 코드의 후반부를 보면 치명적 경고를 별도로 검사합니다.

일반적인 경고 중에서도 특히 위험한 것들이 있습니다. 예를 들어 root 계정 관련 경고나 password 관련 경고는 치명적입니다.

이런 경고가 하나라도 있으면 점수가 90점 이상이어도 배포를 차단합니다. 리스트 컴프리헨션으로 경고 메시지에 'root'나 'password'가 포함된 항목을 필터링합니다.

발견되면 상위 3개를 출력하고 실패 코드를 반환합니다. 실무 적용 시나리오 실제 기업에서는 이렇게 운영합니다.

개발 환경은 80점 이상, 스테이징 환경은 85점 이상, 프로덕션 환경은 90점 이상으로 단계별 기준을 둡니다. 개발 초기에는 느슨하게 하다가, 실제 서비스로 갈수록 엄격하게 만드는 것입니다.

또한 정책 위반 시 자동으로 지라 티켓을 생성하거나, 담당자에게 이메일을 보내는 추가 자동화도 구축합니다. 예를 들어 보안 점수가 90점 미만으로 떨어지면, "긴급: 보안 점수 개선 필요"라는 제목의 티켓이 자동으로 생성됩니다.

정책 수립 시 주의사항 초보자들이 주의할 점이 있습니다. 첫째, 너무 높은 기준은 오히려 역효과를 낳습니다.

현실적으로 달성 가능한 점수를 설정하세요. 만약 현재 평균이 70점인데 갑자기 95점을 요구하면 아무도 배포하지 못합니다.

단계적으로 기준을 높여가는 것이 좋습니다. 둘째, 예외 처리 절차가 필요합니다.

긴급 패치를 해야 하는데 보안 점수 때문에 막히면 곤란합니다. 승인 프로세스를 만들어서 특정 상황에서는 일시적으로 정책을 우회할 수 있게 하세요.

셋째, 정책 위반 이유를 로깅하세요. 누가, 언제, 왜 정책을 위반했는지 기록해두면 나중에 분석할 때 유용합니다.

문화의 변화 정책을 도입한 후 김개발 씨의 팀은 큰 변화를 겪었습니다. 처음에는 "배포가 안 돼요!"라는 불만이 많았습니다.

하지만 몇 주가 지나자 개발자들이 코드를 작성할 때부터 보안을 고려하기 시작했습니다. 배포 전에 미리 보안 스캔을 로컬에서 돌려보고, 문제를 사전에 해결하는 문화가 생겼습니다.

3개월 후, 팀의 평균 보안 점수는 92점으로 올라갔습니다. 무엇보다 보안 사고가 단 한 건도 발생하지 않았습니다.

정책은 가이드레일 박시니어 씨가 회고 미팅에서 말했습니다. "정책은 족쇄가 아니라 가이드레일입니다.

차가 도로를 벗어나지 않게 도와주는 거죠." 명확한 보안 정책을 수립하면 팀 전체의 보안 수준이 향상됩니다. 여러분도 실현 가능한 기준부터 시작하여 점진적으로 개선해나가세요.

실전 팁

💡 - 처음에는 낮은 기준으로 시작하여 단계적으로 높이세요

  • 정책 위반 로그를 남겨 추세를 분석하세요
  • 긴급 상황을 위한 예외 승인 프로세스를 마련하세요

6. 패치 결과 MR 필수 워크플로우

김개발 씨는 보안 취약점을 발견하고 수정했지만, 팀원들에게 알리지 않고 바로 배포했습니다. 나중에 박시니어 씨가 물었습니다.

"뭘 고쳤어요?" 김개발 씨는 정확히 기억나지 않았습니다. 박시니어 씨가 한숨을 쉬며 말했습니다.

"앞으로는 MR(Merge Request)을 필수로 합시다. 리뷰와 기록이 중요해요."

MR 필수 워크플로우는 보안 패치를 적용할 때 반드시 코드 리뷰를 거치고 변경 이력을 남기는 프로세스입니다. 마치 중요한 문서에 결재선을 만들듯이, 모든 보안 변경사항이 검토되고 승인받은 후에만 메인 브랜치에 병합되도록 강제합니다.

다음 코드를 살펴봅시다.

# .github/workflows/security-patch-mr.yml
name: Security Patch MR Workflow

on:
  push:
    branches:
      - 'security-patch/**'  # security-patch로 시작하는 브랜치만

jobs:
  validate-patch:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Run security scan
        run: |
          sudo apt-get update && sudo apt-get install -y lynis
          sudo lynis audit system --quiet

      - name: Check score improvement
        run: |
          python scripts/compare_security_scores.py

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref_name }}
          title: "[보안 패치] 취약점 수정: ${{ github.sha }}"
          body: |
            ## 보안 패치 내용
            - 이전 점수: [자동 입력]
            - 현재 점수: [자동 입력]
            - 수정된 취약점: [자동 생성]

            ## 체크리스트
            - [ ] 보안 스캔 통과 (90점 이상)
            - [ ] 코드 리뷰 완료
            - [ ] 테스트 통과

            **리뷰어: @security-team**
          labels: security, high-priority
          reviewers: security-team

      - name: Require approval
        run: |
          echo "PR 생성 완료. 승인 대기 중..."

김개발 씨는 어느 금요일 오후, 보안 스캔 결과에서 SSH 설정 취약점을 발견했습니다. 간단한 수정이라고 판단한 그는 설정 파일을 고치고 바로 메인 브랜치에 푸시했습니다.

주말 동안 별 문제가 없었기에 안심했습니다. 하지만 월요일 아침, 팀 회의에서 박시니어 씨가 물었습니다.

"금요일에 SSH 설정이 바뀌었던데, 무슨 일이었나요?" 김개발 씨는 당황했습니다. "아, 그게...

취약점 수정이었는데, 정확히 뭘 고쳤는지..." 박시니어 씨가 진지한 표정으로 말했습니다. "보안은 특히 민감해요.

혼자 판단해서 바로 배포하면 위험합니다. 앞으로는 MR을 필수로 만들겠습니다." MR 필수 워크플로우란? 쉽게 비유하자면, MR 필수 워크플로우는 은행의 이중 확인 시스템과 같습니다.

고액 이체를 할 때 담당자가 입력하고 관리자가 승인해야 처리되듯이, 보안 패치도 작성자가 제출하고 리뷰어가 승인해야 메인 브랜치에 반영됩니다. 이는 실수를 방지하고 투명성을 보장합니다.

MR 워크플로우의 핵심은 자동화와 강제입니다. 특정 브랜치에 푸시하면 자동으로 Pull Request가 생성되고, 승인 없이는 병합할 수 없게 설정합니다.

혼자 작업의 위험성 개인이 보안 패치를 리뷰 없이 배포하던 시절의 문제점을 생각해봅시다. 첫째, 실수할 가능성이 높습니다.

SSH 포트를 변경하면서 방화벽 규칙도 함께 수정해야 하는데, 깜빡하면 서버에 접속할 수 없게 됩니다. 둘째, 변경 이유와 내용이 기록되지 않습니다.

몇 달 후 "왜 이렇게 설정했지?"라는 질문에 답할 수 없습니다. 셋째, 팀원들이 변경사항을 모릅니다.

누군가 이전 설정 기준으로 다른 부분을 수정하면 충돌이 발생합니다. 자동 MR 생성의 위력 MR 필수 워크플로우를 도입하면 이런 문제가 해결됩니다.

개발자가 security-patch로 시작하는 브랜치에 코드를 푸시하면, GitHub Actions가 자동으로 보안 스캔을 실행하고 Pull Request를 생성합니다. PR 본문에는 이전 보안 점수와 현재 점수가 자동으로 기재되고, 수정된 취약점 목록도 포함됩니다.

무엇보다 중요한 것은 reviewers 필드에 security-team을 지정하여 보안 전문가의 리뷰를 필수로 만든다는 점입니다. 승인이 없으면 병합 버튼이 비활성화됩니다.

워크플로우 코드 분석 위의 YAML 코드를 단계별로 살펴보겠습니다. on 섹션에서 브랜치 이름 패턴을 지정합니다.

'security-patch/**'는 security-patch/fix-ssh-config, security-patch/update-firewall 같은 브랜치에만 이 워크플로우가 동작한다는 의미입니다. validate-patch 작업은 먼저 보안 스캔을 실행합니다.

그다음 compare_security_scores.py 스크립트로 이전 스캔 결과와 비교하여 점수가 실제로 개선되었는지 확인합니다. 만약 패치를 적용했는데 점수가 그대로거나 오히려 떨어졌다면, 뭔가 잘못된 것입니다.

create-pull-request 액션은 자동으로 PR을 생성합니다. title에는 커밋 해시를 포함하여 고유성을 보장하고, body에는 체크리스트와 리뷰어 멘션을 자동으로 넣습니다.

labels로 security와 high-priority를 달아 우선순위를 높입니다. 점수 비교 스크립트 compare_security_scores.py는 어떻게 동작할까요?

이 스크립트는 Git 히스토리에서 이전 커밋의 보안 리포트를 가져와 현재 리포트와 비교합니다. 예를 들어 이전 점수가 82점이었고 현재 점수가 91점이면 "+9점 개선"이라고 출력합니다.

만약 점수가 떨어졌다면 경고 메시지를 출력하고 실패 코드를 반환하여 PR 생성을 막습니다. 실무 프로세스 실제로 이 워크플로우를 사용하는 과정을 따라가 봅시다.

금요일 오후, 김개발 씨가 다시 SSH 취약점을 발견했습니다. 이번에는 브랜치를 생성합니다.

git checkout -b security-patch/fix-ssh-root-login SSH 설정 파일을 수정하고 커밋한 후 푸시합니다. 몇 초 후, GitHub Actions가 자동으로 동작합니다.

보안 스캔이 실행되고, 점수가 82점에서 93점으로 올랐다는 것을 확인합니다. 자동으로 PR이 생성되고, 슬랙에 알림이 갑니다.

"@security-team 새로운 보안 패치 PR이 생성되었습니다. 리뷰 부탁드립니다." 박시니어 씨가 PR을 열어봅니다.

변경된 코드를 살펴보고, 보안 점수 개선을 확인한 후 코멘트를 남깁니다. "좋습니다.

하지만 방화벽 규칙도 함께 수정해야 할 것 같네요." 김개발 씨가 추가 커밋을 푸시하고, 박시니어 씨가 최종 승인합니다. 월요일 아침에 병합됩니다.

강제력 부여하기 GitHub 저장소 설정에서 브랜치 보호 규칙을 추가해야 합니다. Settings > Branches > Branch protection rules에서 메인 브랜치에 "Require pull request reviews before merging"과 "Require status checks to pass before merging"을 활성화합니다.

이렇게 하면 리뷰 승인 없이는 절대 병합할 수 없습니다. 주의사항 초보자들이 주의할 점이 있습니다.

첫째, 리뷰어를 명확히 지정하세요. reviewers 필드가 비어있으면 아무도 리뷰하지 않을 수 있습니다.

둘째, 긴급 패치의 경우 신속한 리뷰 프로세스가 필요합니다. 주말에도 대응할 수 있는 온콜 담당자를 지정하세요.

셋째, 자동 생성된 PR의 본문을 개발자가 추가로 편집할 수 있게 하세요. 컨텍스트를 더 상세히 설명하면 리뷰가 빨라집니다.

투명성과 책임감 MR 필수 워크플로우를 도입한 후, 김개발 씨의 팀은 모든 보안 변경사항이 명확하게 기록되었습니다. 6개월 후 감사를 받을 때, PR 이력을 보여주며 "모든 보안 패치는 리뷰를 거쳤습니다"라고 자신 있게 말할 수 있었습니다.

보안 패치에 MR을 필수로 만들면 팀의 투명성과 책임감이 높아집니다. 여러분도 오늘부터 시작해보세요.

실전 팁

💡 - 브랜치 보호 규칙으로 강제력을 부여하세요

  • PR 템플릿을 활용하여 일관된 형식을 유지하세요
  • 긴급 패치를 위한 빠른 리뷰 프로세스를 마련하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#Security#Lynis#OpenSCAP#GitHubActions#DevOps,보안,자동화

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.