InfraGuard

보안 진단

여러 서버에 루트킷·하드닝 점검을 한 번에 실행해 서버별 보안 등급(A–F)을 매기고, 위급 항목은 자동으로 문제 해결 케이스로 승격합니다.

개요 — 무엇을 하나

보안 진단(Security Diagnostics) 은 등록된 서버들에 루트킷 흔적 점검 + 시스템 하드닝 점검을 실행하고, 그 결과를 0–100 점수 + A–F 등급으로 채점해 한 화면에서 서버 함대(fleet) 전체의 보안 자세를 보여줍니다. 각 점검이 무엇을·왜·어떻게 고치는지 해설을 붙이고, 불합격이면서 함의가 위급(fail + crit)한 항목은 자동으로 문제 해결 케이스로 승격합니다(디둡 + 이후 통과 시 자동 해소).

핵심 구성:

프로브를 root 로 실행하면 /etc/shadow·sudoers·sshd -T 같은 특권 점검까지 수행합니다. root 가 아니면 특권 점검은 skip 으로 강등되어 점수 분모에서 제외됩니다(불이익 없음).

언제 쓰나

화면 구성

영역 내용
KPI 스트립 플릿 평균 점수 · 점검 호스트 수 · 미해결 위급 케이스 · 불합격 항목 합계 · 최근 점검 시각
등급 분포 A/B/C/D/F 호스트 수 히스토그램
영역별 점수 9개 영역 롤업(전 호스트 평균, 카탈로그 순서, tone 색)
점수 추이 최근 60개 스캔 시간순 라인
호스트별 등급 호스트 카드 그리드 — 점수가 낮은(위험) 순 정렬. 각 카드에 등급·pass/warn/fail/crit 카운트·접수 경로·점검 시각. 클릭 → 리포트 모달
리포트 모달 종합 점수·등급·요약(OS/커널) + 점검 상세(무엇/왜/조치/근거) + 승격 케이스 링크

상단 도구: [점검 실행](다중 서버 원격 실행) · [결과 붙여넣기] · 새로고침 · 자동 갱신 토글 · 전체화면.

사용법

접수 4트랙

infr_sec_scan.source 는 접수 경로를 4가지로 구분합니다.

source 진입점 방법
emulate [점검 실행] 모달 인스턴스를 골라 원격 프로브 배포·실행. instance_id 를 실은 [붙여넣기]/[remote emulate apply]도 emulate 로 기록
paste [결과 붙여넣기] 모달 대상에서 직접 실행한 프로브 JSON 을 인스턴스 연결 없이 붙여넣기
api POST /api/v1/security/scan 각 호스트 크론이 프로브를 실행 → API 키로 자동 푸시(리포터 트랙)
manual 폴백 source 값이 유효하지 않을 때의 기본값

1) 다중 서버 원격 실행 — 비동기 폴링

동기 전체 실행은 다수 호스트에서 PHP 실행시간을 초과하므로, 등록 → 워커(fire-and-forget) → 폴링 패턴을 씁니다.

  1. [점검 실행] → 대상 인스턴스 체크(도달 가능/불가 표시).
  2. POST /security/diagnostics/run → 진행 레코드 생성 후 run_id 즉시 반환(Cache, TTL 30분).
  3. 프런트가 POST /security/diagnostics/run-worker/{runId} 를 await 없이 호출 → 워커가 set_time_limit(0)·ignore_user_abort 로 전 호스트를 순차 실행(호스트 하나 실패해도 나머지 계속).
  4. 프런트는 GET /security/diagnostics/run-status/{runId} 를 폴링해 진행률(done/total/ok)을 갱신.

도달 불가로 표시된 인스턴스는 run_report 수집기 노드와 매칭되지 않아 원격 실행할 수 없습니다. 대상에서 프로브를 직접 실행해 [결과 붙여넣기]로 접수하세요. 원격 실행 배선은 원격 에뮬레이트 참조.

2) 결과 붙여넣기 / JSON 접수

대상 호스트에서 프로브를 실행하고 그 JSON 을 붙여넣습니다.

# 대상 호스트에서(권장: root)
node infr_seccheck_probe.cjs --min

--min 은 1행 JSON 출력입니다. 출력 형태:

{
  "host": "web-01",
  "generated_at": "2026-08-13T03:30:00Z",
  "os": "Rocky Linux 9.3",
  "kernel": "5.14.0-362.el9",
  "root": true,
  "checks": [
    { "key": "ssh_root_login", "category": "ssh", "status": "fail", "severity": "crit", "detail": "PermitRootLogin yes", "evidence": "/etc/ssh/sshd_config:32" },
    { "key": "svc_firewall",   "category": "services", "status": "pass", "severity": "crit", "detail": "firewalld active" }
  ],
  "summary": {}
}

POST /security/diagnostics/ingest 로 접수됩니다. 인스턴스를 연결하면 source=emulate, 연결하지 않으면 source=paste 로 기록됩니다.

3) API 리포터(자동화)

각 호스트 크론이 프로브를 실행해 결과를 InfraGuard 로 푸시합니다. API 키는 Administration ▸ API 발급에서 발급하고, 키에 scan.write 스코프가 있어야 합니다.

30 3 * * *  node /opt/infr/infr_seccheck_probe.cjs --min | curl -sS -X POST https://infraguard.example.com/api/v1/security/scan -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" --data @-

응답(201):

{ "ok": true, "scan_id": 812, "host": "web-01", "score": 74, "grade": "C" }

채점 방법 — SecurityCheckbook

점수는 각 점검의 가중치와 상태를 합산한 비율입니다.

상태 계수(STATUS_FACTOR) — 이 계수만 점수에 반영되고 skip/info 는 분모에서 제외:

상태 계수
pass 합격 1.0
warn 주의 0.6
fail 불합격 0.0
skip·info 점수 제외

심각도 배수(SEV_MUL) — 불합격 시 함의가 클수록 점수 영향 확대:

fail_severity 배수
crit 위급 1.6
warn 주의 1.0
info 정보 0.5

항목 가중치 w = 점검weight(1–3) × 심각도배수, 종합 점수 = round(100 × Σ(w × 상태계수) / Σw). 영역별 점수도 같은 공식을 영역 단위로 계산합니다.

등급·톤:

점수 등급 톤
≥ 90 A good
≥ 80 B good
≥ 70 C warn
≥ 60 D warn
< 60 F crit

(등급은 A/B/C/D/F, 톤은 good ≥ 80 · warn ≥ 60 · crit < 60.)

9개 영역 · 36개 점검

각 영역은 표시 중요도(weight)와 아이콘을 가지며, 실제 채점은 위의 점검별 weight × 심각도배수로 이루어집니다.

영역 라벨 점검 수 대표 점검(불합격 시 함의)
rootkit 루트킷·멀웨어 6 rk_known_files·rk_ld_preload·rk_hidden_proc(crit)
patch 패치·업데이트 3 pt_security_updates·pt_eol_os(crit)
ssh SSH 설정 5 ssh_root_login·ssh_empty_pass(crit)
accounts 계정·권한 5 acc_uid0·acc_empty_pass(crit)
network 네트워크 노출 3 net_legacy_services·net_db_exposed(crit)
filesystem 파일시스템 권한 5 fs_shadow_perm(crit)·fs_world_writable·fs_suid(warn)
hardening 시스템 하드닝 5 hd_selinux·hd_aslr·hd_syncookies(warn)
services 서비스·방화벽 2 svc_firewall(crit)·svc_unnecessary(info)
logging 감사·로깅 2 log_auditd·log_syslog(warn)

미등록 key 는 other 영역으로 흡수됩니다. 개별 점검의 "무엇을/왜/조치" 해설은 리포트 모달에서 seccheck.check.* 로 렌더됩니다.

위급 항목 → 문제 해결 케이스 승격

접수마다 reconcileCases() 가 호스트의 케이스를 정합화합니다.

승격된 케이스는 리포트 모달의 각 항목에 "문제 해결로 승격됨" 배지 + [케이스 보기] 링크로 나타납니다. 케이스는 Security ▸ 문제 해결 보드에서 다룹니다.

스캔을 삭제해도(POST /security/diagnostics/delete) 승격된 케이스는 독립적으로 유지됩니다(같이 지워지지 않음).

데이터 소스

소스 역할
infr_sec_scan(000766) 스캔 결과 저장(호스트별 1회). score·grade·pass/warn/fail/crit/total_count·category_scores·summary·findings JSON. 인덱스 (host,scanned_at)·(instance_id,scanned_at)·grade·score
infr_instance 대상 인스턴스(alias/hostname 으로 host → instance_id 해석)
infr_incident / infr_incident_event 승격된 위급 케이스와 타임라인
infr_api_key API 리포터 인증(scope scan.write)
Cache 대시보드 집계 20초 캐시(infr:sec:diag:build) · 비동기 실행 진행 레코드(TTL 1800초)
infr_seccheck_probe.cjs 원천 프로브(로컬 read-only)

권한

레벨 체계 일반은 RBAC 레벨 참조(infr.level:N 미들웨어).

문제해결 · FAQ

Q. [점검 실행]에서 인스턴스가 "도달 불가"로 뜹니다. 해당 인스턴스가 run_report 수집기 노드와 매칭되지 않아 원격 실행 배선이 없는 경우입니다. 대상에서 프로브를 직접 실행하고 [결과 붙여넣기]로 접수하세요.

Q. "점검 항목(checks)이 없습니다" 오류. 붙여넣은 JSON 에 checks(또는 items) 배열이 없거나 각 항목에 key 가 없습니다. infr_seccheck_probe.cjs --min 출력을 그대로 붙여넣으세요.

Q. 점수가 낮은데 fail 개수가 적습니다. 심각도 배수 때문입니다. crit 불합격 1건은 warn 1건보다 점수에 1.6배 크게 반영됩니다. 영역별 점수와 리포트 모달로 어느 항목이 크게 깎는지 확인하세요.

Q. 특권 점검이 전부 skip 입니다. 프로브를 root 가 아닌 계정으로 실행했기 때문입니다. /etc/shadow·sudoers·sshd -T 점검은 root 권한이 필요합니다. skip 은 점수 분모에서 제외되므로 불이익은 없지만, 완전한 진단을 위해 root(또는 게이트웨이 경유)로 실행하세요.

Q. 실행이 오래 걸리는데 브라우저를 닫아도 되나요? 네. 워커는 fire-and-forget 로 돌고 ignore_user_abort 로 요청 중단에도 계속 진행합니다. 다시 열어 run-status 폴링으로 결과를 확인하거나, 완료 후 대시보드에 반영된 등급을 보면 됩니다. 진행 레코드는 30분 보관됩니다.

Q. 케이스가 자동으로 사라졌습니다. 이후 점검에서 해당 항목이 더 이상 fail+crit 이 아니면(수정 완료·항목 미검출) 엔진이 케이스를 resolved 로 자동 해소합니다. 정상 동작입니다.

관련 항목

보안 진단 — 점검 결과와 등급
보안 진단 — 점검 결과와 등급