[Linux] Lynis로 보안 취약점을 점검하고 Claude code로 Hardening guide를 작성

Lynis는 리눅스 서버의 보안 취약점을 점검하고 System Hardening 가이드라인을 제공해 주는 오픈소스 툴입니다. OFFLINE 서버에서도 사용할 수 있도록 무설치 방식의 사용법을 알아보겠습니다.

  1. https://cisofy.com/downloads/lynis/ 접속하여 최신 버전을 Binary Tarball을 다운로드합니다. 2026년5월12일 현재 최신 버전을 3.1.6입니다.
    $ cd /usr/local/
    $ wget https://downloads.cisofy.com/lynis/lynis-3.1.6.tar.gz
    $ tar xfvz lynis-3.1.6.tar.gz
    $ cd lynis
    
  2. Lynis 명령어를 실행합니다. 실무에 사용할 수 있도록 lynis 명령어의 옵션에 대해서도 알아보겠습니다.
    $ ./lynis audit system
    
    • --report-file: lynis-report.dat의 위치를 변경합니다.
    • --log-file: lynis.log의 위치를 변경합니다.
    • --profile: 기본으로 설정된 /usr/local/lynis/default.prf 대신에 사용할 사용자 프로파일을 지정합니다.
    • --pentest: 대화형 확인 과정을 생략하고 전체 스캔을 수행합니다.
    • --quick: 각 테스트 단계마다 엔터를 누를 필요 없이 바로 넘어갑니다.
    • --tests: 점검하고 싶은 Test-ID를 쉼표(,)로 구분하여 입력합니다.
    • --skip-test: 제외하고 싶은 Test-ID를 쉼표(,)로 구분하여 입력합니다.
  3. 점검 리포트는 디폴트로 /var/log 디렉토리에 생성되며, Lynis 감사 결과를 확인해 봅니다.
    $ ls -l /var/log/lynis*
    -rw-r-----. 1 root root 398487 May 12 08:58 /var/log/lynis.log
    -rw-r-----. 1 root root  80752 May 12 08:58 /var/log/lynis-report.dat
    $ grep -iE "warning|suggestion" /var/log/lynis.log
    ....[test: Test-ID]....
    $ ./lynis show details Test-ID
    
    • lynis.log : 프로그램이 각 테스트를 어떻게 수행했는지, 왜 특정 테스트를 건너뛰었는지 등 기술적인 과정이 기록됩니다. 감사 과정에서 발생한 모든 세부 디버깅 정보가 담기며 문제가 생겼을 때 확인합니다. 특정 보안 체크 항목이 왜 [ SKIPPED ] 되었는지 궁금하다면 이 파일을 뒤져봐야 합니다.
    • lynis-report.dat : 일반적인 텍스트 문서라기보다 “키=값(Key=Value)” 형태의 데이터 파일로 Lynis의 모든 검사 결과를 요약한 ‘데이터베이스’와 같습니다. 실제 감사 결과(점수, 발견된 취약점, 제안 사항 등)가 기계가 읽기 좋은 형태로 저장됩니다. 나중에 이 데이터를 바탕으로 그래프를 그리거나, 다른 도구로 분석할 때 사용됩니다. 화면에 나왔던 Suggestions(제안)이나 Warnings(경고)만 다시 보고 싶을 때 이 파일을 열어보면 편리합니다.
  4. 조치 후 해당 테스트만 다시 수행해 봅니다.
    $ ./lynis audit system --tests <Test-ID> --log-file=/var/log/lynis_<Test-ID>.log
    
  5. 본인의 서버 환경에 맞도록 테스트가 필요없는 항목(Test-ID)을 등록하고, 설정을 확인해 봅니다. 이 글을 참고하시면 기본적인 lynis 설정방법을 알 수 있습니다. Lynis security controls 페이지에서 Test-ID 정보를 확인할 수 있습니다.
    $ ./lynis show profiles
    /usr/local/lynis/default.prf
    $ vi /usr/local/lynis/default.prf
    # Skip a test (one per line)
    # 인터넷이 차단되어 있고 내부 DNS서버도 사용하지 않는 경우, /etc/resolv.conf 파일과 관련된 테스트 제외
    skip-test=NETW-2705
    # 가상 브리지 인터페이스를 무차별 모드(promiscuous) 검사에서 제외
    skip-test=NETW-3015:virbr0
    skip-test=NETW-3015:virbr0-nic
    $ ./lynis show settings --brief
    
  6. Claud Code를 사용해서 lynis 점검 리포트를 분석하고 Hardening 가이드를 제공받습니다. 아래는 Claude에서 lynis 점검 리포트의 분석을 요청할 때 사용한 claude.md 파일입니다.
    # 프로젝트 개요
    - Lynis 툴로 Linux 서버의 보안 취약점 점검 결과를 분석해서 System Hardening 가이드를 제공하는 프로젝트
    - 동일한 입력으로 다시 분석할 때 **항상 같은 결과**가 나오도록 모든 결정사항을 본 문서에 기록한다.
    
    # 입력 파일 규칙
    - 작업 디렉토리에 호스트별로 다음 두 파일을 배치한다.
      - `lynis-report.<호스트네임>..dat`  (필수, 파싱 대상)
      - `lynis.<호스트네임>..log`         (선택, FILE-7524 상세 보강용)
    - 구분자는 점(`.`)이며, 호스트네임에 점은 사용할 수 없다(필드 구분자와 충돌). 하이픈은 허용.
    - 두 파일의 `<호스트네임>.` 부분이 동일해야 한 쌍으로 인식된다.
    - 다중 호스트 처리: 같은 디렉토리에 여러 쌍을 두면 파일명 사전순(`sorted`)으로 누적 처리한다.
    - Hardening 가이드 파일의 "분석 일자" 컬럼 값으로 yyyymmdd를 사용한다.
    
    # 분석 도구
    - 실행 스크립트: `generate_hardening_guide.py` (이 프로젝트의 단일 소스)
    - 요구 환경: Python 3.10+, `openpyxl` (현재 검증: Python 3.12.6 / openpyxl 3.1.5)
    - 실행 명령 (PowerShell):
      ```
      $env:PYTHONIOENCODING = "utf-8"
      python generate_hardening_guide.py --input-dir . --output @SystemHardeningGuide.xlsx
      ```
    - 스크립트는 외부 네트워크/LLM 호출 없이 **정적 매핑 테이블**(`GUIDE`, `SSH_OPTION_KO`, `SYSCTL_KO`, `CATEGORY_BY_PREFIX`)만 사용한다. 따라서 같은 입력 + 같은 스크립트 버전이면 결과는 비트 단위로 동일하다.
    
    # Lynis 분류체계 (test ID prefix 기준)
    - 스크립트 상단의 `CATEGORY_BY_PREFIX` 가 단일 진실원본(SoT).
    - 매핑되지 않은 prefix 가 새로 등장하면 prefix 자체가 "분류" 컬럼에 그대로 출력되며, 이 표를 보강해야 한다.
    
    | prefix | 분류        | prefix | 분류                  |
    |--------|-------------|--------|-----------------------|
    | BOOT   | 부팅 서비스 | LOGG   | 로깅/감사             |
    | KRNL   | 커널        | BANN   | 배너/경고문구         |
    | MEM    | 메모리/프로세스 | ACCT   | 계정 감사         |
    | AUTH   | 사용자/인증 | TIME   | 시간/NTP              |
    | FILE   | 파일시스템  | CRYP   | 암호화                |
    | USB    | 스토리지(USB) | MACF | 강제접근제어(SELinux) |
    | STRG   | 스토리지    | FINT   | 파일 무결성           |
    | NAME   | 이름 서비스(DNS) | TOOL | 자동화 도구       |
    | PKGS   | 패키지/소프트웨어 | HOME | 홈 디렉토리       |
    | NETW   | 네트워크    | HRDN   | 시스템 하드닝         |
    | MAIL   | 메일        | SNMP   | SNMP                  |
    | FIRE   | 방화벽      | SSH    | SSH                   |
    
    (전체 목록은 스크립트의 `CATEGORY_BY_PREFIX` 참고)
    
    # 출력 파일 (Excel) 구조
    - 파일명: `@SystemHardeningGuide.xlsx`
    - 시트는 **2개**로 고정:
      1. `Hardening 가이드` — 본문 (단일 시트, 평탄 구조)
      2. `요약` — 호스트별 메타데이터/통계
    
    ## 시트 1: Hardening 가이드 — 컬럼 정의
    | # | 컬럼명           | 데이터 소스                                              |
    |---|------------------|----------------------------------------------------------|
    | 1 | 분석 일자        | 입력 파일명의 `` 접미사                        |
    | 2 | 호스트네임       | `hostname`                                               |
    | 3 | OS               | `os_fullname` (예: RHEL 7.9 (Maipo))                     |
    | 4 | 분류             | test ID prefix → `CATEGORY_BY_PREFIX`                    |
    | 5 | Lynis ID         | 예: SSH-7408, KRNL-6000                                  |
    | 6 | 점검 항목        | 한글 설명 (`GUIDE[id].title_ko` 또는 suggestion 원문)    |
    | 7 | 현재값           | `details[].value` 또는 warning 의 부가정보               |
    | 8 | 권장값           | `details[].prefval`                                      |
    | 9 | 위험도           | warning → High / suggestion → Medium                     |
    | 10| 조치 명령어/설정 | `GUIDE[id].fix` (정적 매핑)                              |
    | 11| 비고             | warning / suggestion / sshd 옵션 / sysctl 키 / 권한 불일치 |
    | 12| 처리여부         | 수동 입력용 — 드롭다운(Y/N/NA), 기본값 공란                |
    
    - 헤더 1행 고정(`freeze_panes = "A2"`), 자동 필터 적용.
    - 위험도 셀: High(주황), Medium(노랑) 색상 강조.
    - 처리여부 셀: Excel `DataValidation(type="list", formula1='"Y,N,NA"', allow_blank=True)` 적용 → Y(조치 완료) / N(미조치) / NA(해당 없음) 중 드롭다운 선택. 잘못된 값 입력 시 경고. 가운데 정렬.
    
    ## 시트 2: 요약 — 컬럼 정의
    | 호스트네임 | OS | 커널 | Lynis 버전 | Hardening Index | Warnings | Suggestions | 점검 행 수 |
    
    # 동적 항목 펼치기 규칙
    같은 Lynis ID 가 여러 세부 항목으로 나오는 경우, 다음 규칙으로 행을 분리한다.
    
    - **SSH-7408**: `details[]=SSH-7408|sshd|field:...;prefval:...;value:...` 를 옵션 단위로 1행씩.
      - 한글 설명: `SSH_OPTION_KO[field]` (없으면 details 의 desc).
    - **KRNL-6000**: `details[]=KRNL-6000|sysctl|field:...;prefval:...;value:...` 를 sysctl 키 단위로 1행씩.
      - 한글 설명: `SYSCTL_KO[field]` (없으면 details 의 desc).
    - **FILE-7524**: `.log` 파일에서 `permissions of file  are not matching expected value (NNN != MMM)` 패턴을 추출하여 파일 단위로 1행씩.
      - `.log` 가 없으면 suggestion 행 1개만 추가.
    - **NETW-3200, AUTH-9286 등** suggestion 안에서 동일 ID 가 부가정보(`extra`)만 다르게 여러 번 등장: `(test_id, extra)` 단위로 중복 제거하면서 모두 행으로 추가.
    
    # 네트워크 환경 분기 표기 규약
    - 일부 점검 항목(외부 통신 전제)은 호스트의 네트워크 환경에 따라 적절한 조치가 달라진다.
    - 해당 항목의 `GUIDE[id].fix` 본문은 다음 라벨로 구간을 분리하여 작성한다:
      - `[폐쇄망/에어갭]` — 인터넷 단절, 내부망 전용
      - `[내부 DNS 사용]` — 내부 DNS 서버만 사용 (선택, NETW-2705/NAME-4028 만 해당)
      - `[외부통신 가능]` — 인터넷/외부 서비스 접근 가능
      - `공통:` — 환경 무관 적용 사항
    - 폐쇄망 구간에는 가능한 한 Lynis 점검 예외처리 안내(`skip-test=`)를 포함한다.
    - 현재 분기 표기를 적용한 항목 (2026-05-13 기준):
      `NETW-2705`, `NAME-4028`, `PKGS-7420`, `LOGG-2154`, `CRYP-7902`, `HRDN-7230`
    - 신규 ID 추가 시, 외부 통신/서비스 의존성이 있다면 같은 라벨 체계로 작성한다.
    
    # 매핑 테이블 확장 정책
    - 새 Lynis ID 가 결과에 등장하면 `GUIDE` 딕셔너리에 다음 형태로 추가한다:
      ```python
      "ABCD-1234": {
          "title_ko": "한글 점검 항목명",
          "fix": "권장 명령어/설정 (멀티라인 가능)",
      },
      ```
    - 매핑이 없으면 점검 항목은 Lynis 원문(영문)이 들어가고, 조치 컬럼은 `https://cisofy.com/lynis/controls//` 안내 링크가 자동으로 채워진다 — 이는 **누락 신호**이므로 발견 즉시 매핑을 보강한다.
    - prefix 가 새로우면 `CATEGORY_BY_PREFIX` 도 함께 보강한다.
    
    # 결과 재현 체크리스트
    1. 입력 파일 (`lynis-report...dat`, `lynis...log`) 가 동일한가?
    2. `generate_hardening_guide.py` 의 git/파일 해시가 동일한가?
    3. Python / openpyxl 버전이 동일한가? (openpyxl 의 스타일 출력은 버전 간 미세 차이 가능)
    4. 출력 파일을 비교: `Hardening 가이드` 시트의 모든 셀 값이 일치해야 한다 (스타일은 비교 제외).
    
    # 점검 결과
    - 분석 대상 일자(파일명 yyyymmdd): **20260513**
    - 공통 환경: RHEL 7.9 (Maipo) / Kernel 3.10.0-1160.133.1.el7.x86_64 / Lynis 3.1.6
    - 출력 파일: `@SystemHardeningGuide.xlsx` (12 컬럼 × 134 데이터 행)
    
    | 호스트     | Hardening Index | Warnings | Suggestions | 출력 행 |
    |------------|-----------------|----------|-------------|---------|
    | non-w-dev  | 64              | 4        | 43          | 66      |
    | Non-W-PRD  | 65              | 4        | 45          | 68      |
    | **합계**   | -               | 8        | 88          | **134** |
    
    - 전체 등장 Lynis ID: **35개** (모두 `GUIDE` 매핑 보유 — 누락 0)
    - 신규로 매핑 보강한 ID (2026-05-13): `NETW-2705`, `FILE-6354`, `HRDN-7230`
    
    ## 변경 이력
    - 2026-05-13: 초기 가이드 생성. SSH-7408/KRNL-6000/FILE-7524 동적 펼치기 도입.
      네트워크 환경 분기 표기(`[폐쇄망/에어갭]` / `[외부통신 가능]`) 6개 항목 적용.
    - 2026-05-14: 입력/출력 파일명 규약을 점(`.`) 구분자 기반으로 변경
      (`lynis-report...dat`, `lynis...log`,
       출력 `@SystemHardeningGuide.xlsx`). 통계 변동 없음.
    

    SystemHardeningGuide

  7. crontab을 사용하여 주기적으로 취약점 점검을 실행합니다.
    $ crontab -e
    0 3 1 3,6,9,12 * /usr/local/lynis/lynis audit system --cronjob --log-file /var/log/lynis.$(hostname).$(date +\%Y\%m\%d).log --report-file /var/log/lynis-report.$(hostname).$(date +\%Y\%m\%d).dat > /dev/null 2>&1
    0 3 1 3,6,9,12 * find /var/log/ -type f -name "lynis-*" -mtime +90 -delete > /dev/null 2>&1
    

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다