OPNsense 모니터링 구축

개요

OPNsense 방화벽의 주요 서비스를 Grafana 대시보드로 모니터링하는 시스템 구축.

아키텍처

OPNsense (192.168.1.1)
├── node_exporter (:9100) → Prometheus → Grafana (시스템 메트릭)
├── os-telegraf (:9273) → Prometheus → Grafana (Unbound DNS 메트릭)
├── CrowdSec LAPI (:6060) → Prometheus → Grafana (보안 메트릭)
└── syslog (UDP→loki.lan:1514) → Alloy → Loki → Grafana (방화벽 로그)

컴포넌트 구성

1. Unbound DNS (Telegraf)

  • 플러그인: os-telegraf (OPNsense GUI에서 설치)
  • 설정: Services > Telegraf > Input > Unbound 활성화
  • 출력: Prometheus 형식 (:9273/metrics)
  • 주요 설정: “루트로 실행” 활성화 (unbound-control 권한 해결)
  • Prometheus job: telegraf
  • 수집 메트릭: 쿼리 수, 캐시 히트율, 응답코드, 메모리, 재귀 시간 등

2. CrowdSec

  • 엔드포인트: 192.168.1.1:6060/metrics (내장 Prometheus)
  • Prometheus job: crowdsec
  • 수집 메트릭: 활성 차단 IP, 알림, 버킷, 파서 처리량, Bouncer 요청

3. 방화벽 로그 (Alloy → Loki)

  • OPNsense: System > Settings > Logging > Remoteloki.lan:1514 (UDP, RFC5424)
  • Grafana Alloy (loki.lan):
    • syslog 수신 (UDP:1514)
    • filterlog 파싱 (action, direction, src_ip 추출)
    • GeoIP 변환 (GeoLite2-City.mmdb)
    • Loki로 전송
  • 설정 파일: /etc/alloy/config.alloy
  • GeoIP DB: /etc/alloy/geoip/GeoLite2-City.mmdb

Grafana 대시보드 패널

Unbound DNS 섹션

패널타입쿼리
총 DNS 쿼리Statunbound_total_num_queries{job="telegraf"}
캐시 히트Statunbound_total_num_cachehits{job="telegraf"}
캐시 히트율Stat히트 / (히트 + 미스) * 100
캐시 미스Statunbound_total_num_cachemiss{job="telegraf"}
평균 재귀 시간Statunbound_total_recursion_time_avg{job="telegraf"}
UptimeStatunbound_time_up{job="telegraf"}
DNS 쿼리/초Timeseriesrate(총 쿼리, 캐시 히트, 캐시 미스)
DNS 응답 코드/초TimeseriesNOERROR, NXDOMAIN, SERVFAIL 등
DNS 쿼리 타입/초TimeseriesA, AAAA, PTR, SRV, HTTPS
Unbound 메모리TimeseriesMessage Cache, RRSet, Iterator, Validator

CrowdSec 섹션

패널타입쿼리
활성 차단 IPStatsum(cs_active_decisions{job="crowdsec"})
로컬 알림Statsum(cs_alerts{job="crowdsec"})
활성 버킷Statcs_buckets{job="crowdsec"}
CrowdSec 버전Statcs_info{job="crowdsec"}
활성 차단 (유형별)TimeseriesHTTP/SSH/TCP 스캔별 차단 추이
CrowdSec 처리량/초Timeseries파서, Node, Bouncer 요청

방화벽 로그 섹션

패널타입쿼리
Block vs PassTimeseriesrate({job="opnsense_syslog", action="block/pass"})
차단 방향TimeseriesIn vs Out
차단된 외부 요청 로그Logs{job="opnsense_syslog", action="block", direction="in"}

Prometheus scrape 설정

- job_name: opnsense
  static_configs:
    - targets: ['192.168.1.1:9100']
 
- job_name: telegraf
  static_configs:
    - targets: ['192.168.1.1:9273']
 
- job_name: crowdsec
  static_configs:
    - targets: ['192.168.1.1:6060']

GeoIP 지도 섹션

패널타입쿼리
차단된 요청 지도 (GeoIP)Geomap국가/위도/경도별 차단 횟수
국가별 차단 횟수Table국가명, 국가코드, 차단 횟수

Alloy GeoIP 파이프라인 상세

syslog(UDP:1514) → stage.regex(IP 추출) → stage.geoip(GeoLite2-City) → stage.labels + stage.structured_metadata → Loki

GeoIP 출력 필드

  • labels: geoip_country_name, geoip_country_code (낮은 카디널리티)
  • structured_metadata: geoip_city_name, geoip_location_latitude, geoip_location_longitude (높은 카디널리티)

트러블슈팅 이력

  1. Alloy loki.source.syslog는 RFC5424만 지원 → OPNsense에서 RFC5424 활성화
  2. RFC5424 활성화 전 잔여 RFC3164 메시지로 파싱 에러 → Alloy 재시작으로 해결
  3. labels 속성이 loki.source.syslog에서 미지원 → loki.process + stage.static_labels 사용
  4. 고카디널리티 GeoIP 필드 → stage.structured_metadata로 분리
  5. stage.geoipstage.labels로 명시적 승격 필요 (shared map → labels)

경고 규칙 (Telegram 알림)

경고조건지속심각도
외부 공격 급증차단 5분간 200건 초과5분Critical
DNS 서비스 다운Unbound uptime < 1초1분Critical
DNS 캐시 히트율 저하히트율 < 50%10분Warning
CrowdSec 차단 IP 급증활성 차단 > 20,000개5분Warning
DNS SERVFAIL 급증SERVFAIL rate > 5/초5분Warning

남은 작업

  • Alloy config에서 로그 레벨을 debugwarn으로 변경 (안정화 후)