InfraGuard

IP 연결 (IP Connections)

서버별 원격 peer IP를 포트 강도 도트로 보고, IP를 클릭해 접속 분포·기간 이력·지오/ASN·평판·차단까지 조사하는 host view 콘솔.

개요 — 무엇을 하나

IP 연결은 Infrastructure ▸ Connectivity & Traffic 의 두 번째 화면(host view)입니다. 포트 연결이 "포트 → 서버"를 축으로 보는 반면, 이 화면은 **"서버(호스트) → 원격 peer IP"**를 축으로 봅니다. 각 서버 카드는 그 서버에 붙어 있는 원격 IP들을 연결 강도순으로 나열하고, IP마다 감시 포트별 세션 강도 도트를 보여줍니다. 어느 IP가 어떤 서버의 어떤 포트로 얼마나 붙는지를 한눈에 파악하는 것이 목적입니다.

peer IP를 클릭하면 공용 IP 조사(investigation) 팝업이 열려, 그 IP의 접속 분포(어느 서버·포트·TCP 상태·세션 수), 기간 연결 이력 스파크라인, 지오/ASN/ISP 인리치, 외부 평판 링크, 그리고 차단 목록으로 넘어가는 차단 딥링크까지 한 곳에서 조사할 수 있습니다.

모든 데이터는 레거시 트래픽 DB(legacy_glory)를 읽기 전용으로 조회합니다. 이 화면 자체는 아무것도 쓰지 않습니다 — 차단 같은 쓰기 동작은 팝업의 딥링크로 차단 목록 화면에 넘겨 거기서 수행합니다.

언제 쓰나

화면 구성

히어로 · timer

우상단에 표시 창을 정하는 **timer 토글(1분 / 5분 / 1시간)**과 최신 스냅샷 시각, 수동 새로고침 버튼이 있습니다. timer는 최신 스냅샷을 앵커로 한 조회 창의 길이입니다.

timer 조회 창
1 (기본) 최신 스냅샷 분 1개
5 최신 스냅샷 기준 최근 5분
60 최신 스냅샷 기준 최근 60분

선택한 timer는 localStorage['infr:timer:traffic-ips']에 저장돼 다음 방문에도 유지됩니다. 허용값은 1/5/60뿐이며 그 외 입력은 1로 보정됩니다.

연결 강도 레전드 + 서버 칩

서버(호스트) 카드

선택된 서버마다 카드 하나. 카드 헤더에 호스트명과 peer 수가 있고, 본문은 표 형태입니다.

카드는 드래그 그립으로 순서를 바꿀 수 있고(순서는 infr:order:traffic-ips에 영속), row-span masonry로 재배치되며 20개 단위로 페이지네이션됩니다.

포트 강도 도트 색

세션 수 토큰 색 의미
0 none 흐림 이 포트로 붙지 않음
1–9 good 녹색 낮은 강도
10–49 warn 황색 중간 강도
50+ crit 적색 높은 강도

IP 조사 팝업

공용 믹스인 mixins/ip_detail.js + 파셜 infra/partials/ip-detail-modal.blade.php로 구동됩니다. 두 모드가 있습니다.

  1. sources 모드 — (서버, 포트) 창에 붙은 소스 IP 목록. 포트 연결의 SRC 서클에서 진입합니다.
  2. investigate 모드(이 화면의 기본 진입) — 단일 IP 심층 조사. peer 행 클릭으로 진입합니다.

investigate 모드가 보여주는 것:

영역 내용 원천
요약 KPI 총 세션 · 서버 수 · 포트 수 · 최초/최종 관측 ipSeenSpan()(전체 보존창 MIN/MAX(created_at))
지오 국가·도시·ASN·ISP ip_info_loop (ipEnrichment())
연결 이력 grain(5분/시간별/일별) 스파크라인 롤업 테이블(ipHistory())
접속 분포 서버 × 포트 × TCP 상태별 세션·원격포트·최초/최종 traffic_session_ct (ipBreakdown())

팝업 하단 액션(investigate): IP 복사 · IP 조회(이 페이지에서 ?src_ip=로 재조사) · 평판 ↗(외부 AbuseIPDB 새 탭) · IP 차단(차단 목록으로 ?add= 딥링크).

팝업의 평판 ↗ 버튼은 외부 abuseipdb.com/check/… 링크를 새 탭으로 여는 단순 링큽니다. 화면 안에서 신뢰도 게이지·신고 내역을 보여주는 인앱 평판 팝업은 보안 화면 전용 기능입니다 — IP 평판 조회 참고.

사용법

기본 흐름

  1. timer로 창(1/5/60분)을 정합니다. 짧을수록 "지금 붙어 있는" 것에 가깝습니다.
  2. 서버 칩으로 관심 서버만 남깁니다(기본 전체).
  3. 서버 카드에서 강도 도트가 붉은 peer(50+ 세션)나 여러 포트에 걸친 peer를 찾습니다.
  4. 의심 peer 행을 클릭해 조사 팝업을 엽니다.
  5. 팝업에서 접속 분포로 어느 서버·포트·상태에 붙었는지, 연결 이력으로 지속성(일회성 vs 반복)을, 지오/ASN으로 출신을 확인합니다.
  6. 필요하면 **평판 ↗**로 외부 신고 이력을 보고, 악성이면 IP 차단으로 차단 목록에 넘깁니다.

연결 이력 grain 전환

팝업의 이력 섹션에서 grain을 전환하면 롤업 테이블이 바뀝니다. 기본 grain은 시간별(1hour) — 밀도와 속도의 균형이 좋습니다.

5분(5min)   → traffic_session_ct_5min   · 개별 IP · 최신 스냅샷 −6h 창
시간별(1hour)→ traffic_session_ct_1hour  · 개별 IP · 최신 스냅샷 −3d 창   (기본)
일별(1day)  → traffic_session_ct_1day   · /24 대역 · 최신 스냅샷 −120d 창

일별(1day) 이력은 개별 IP가 아니라 그 IP의 /24 대역(앞 3옥텟, 256개 IP) 기준으로 집계됩니다. 롤업 수집기가 _1day를 C클래스로 저장하기 때문입니다. 팝업은 이때 211.224.128.0/24 같은 CIDR 스코프 라벨을 함께 띄워 범위를 명시합니다. 5분·시간별은 개별 IP 그대로입니다.

딥링크

/infra/traffic/ips?src_ip=203.0.113.10
/infra/traffic/ips?servers=glory,blue

폴링

페이지는 30초마다 /infra/traffic/ips/live를 폴링해 자동 갱신합니다(Reverb 아님 — 수집기가 Laravel 밖에서 DB에 직접 쓰므로 폴링이 합리적). 갱신 시 전체선택이었다면 새로 나타난 서버도 자동 포함하고, 사라진 서버는 선택에서 제거합니다.

데이터 소스

조회 창 앵커링

표시 창은 앱의 now()가 아니라 데이터의 최신 스냅샷(MAX(snapshot_ts))을 앵커로 잡습니다. 수집기의 저장 시계와 앱 시계가 최대 ~9시간까지 어긋날 수 있어, now() 기준이면 좁은 창이 데이터를 벗어나 빈 화면이 되기 때문입니다.

host view 쿼리 (ipPeers)

-- TrafficRepository::ipPeers (파라미터 바인딩)
SELECT server_name, remote_ip, local_port, SUM(session_count) AS total_sessions
  FROM traffic_session_ct
 WHERE snapshot_ts BETWEEN :min AND :max
   AND local_port IN (:ports)          -- OpsPolicy::monitoredPorts()
 GROUP BY server_name, remote_ip, local_port;

감시 포트는 하드코딩이 아니라 운영 정책의 infra_monitored_ports(CSV)에서 옵니다. ipsData() 결과는 20초 캐시됩니다(키 infr:traffic:ips:v1:{timer}:{ports}).

조사 팝업 쿼리

메서드 테이블 하는 일
ipSeenSpan() traffic_session_ct MIN/MAX(created_at) = 진짜 관측 스팬(≈8h 보존창), MAX(snapshot_ts) = 이 IP의 자체 최신 배치(분포 앵커)
ipBreakdown() traffic_session_ct 서버×포트×상태별 세션 합·원격포트·최초/최종 (LIMIT 200)
ipHistory() 롤업 3종 grain별 세션 이력 (LIMIT 400)
ipEnrichment() ip_info_loop 국가·도시·ASN·ISP (없으면 graceful null)

접속 분포는 전역 최신 창이 아니라 그 IP의 자체 최신 스냅샷 배치를 앵커로 잡습니다. 지금은 접속이 없지만 과거에 붙었던 IP도 마지막 관측 시점 기준으로 분포를 복원해 보여주기 위한 설계입니다.

롤업 인덱스 주의 (성능 임계)

ipHistory()는 항상 direction IN ('inbound','outbound')를 명시합니다.

-- _1day 는 t_ip(direction, remote_ip) 복합 인덱스를 쓴다.
-- direction 을 명시하지 않으면 created_at 범위만 스캔 → 12s+.
-- 명시하면 인덱스 적중 → ~20ms (실측 2026-08-06).
SELECT created_at, SUM(session_count) AS sessions
  FROM traffic_session_ct_1day
 WHERE direction IN ('inbound','outbound')
   AND remote_ip = :cclass_key           -- 일별은 /24 앞 3옥텟 키
   AND created_at >= :since
 GROUP BY created_at
 ORDER BY created_at
 LIMIT 400;

롤업은 초대용량(_5min ≈ 209M · _1hour ≈ 112M · _1day ≈ 24M)이라 tight created_at >= 창 + LIMIT이 필수입니다. 팝업 결과는 investigate 25초 · sources 20초 캐시로 보호됩니다.

권한

문제해결 · FAQ

카드가 비어 있고 "이 창에 peer 가 없습니다"만 뜹니다. 최신 스냅샷의 감시 포트에 연결이 없다는 뜻입니다. (1) timer를 5나 60으로 넓혀 보고, (2) 운영 정책의 infra_monitored_ports에 실제 서비스 포트가 등록됐는지, (3) traffic_session_ct에 최근 스냅샷이 들어오는지(수집기 check_ct.js) 확인합니다.

전체가 비고 스냅샷 시각도 없습니다. legacy_glory 커넥션 또는 traffic_session_ct 자체가 비었을 가능성이 큽니다. .env의 legacy_glory DB 접속과 트래픽 수집기 가동을 확인합니다. 조회 실패 시 payload에 error:true가 실립니다.

일별 이력만 "이 대역(/24)의 일별 롤업 이력이 없습니다"로 뜹니다. 일별 집계는 하루 지연될 수 있고 /24 대역 기준입니다. 5분·시간별 grain을 먼저 확인하세요. 대역에 아직 롤업이 없으면 정상입니다.

IP를 클릭했는데 접속 분포가 비어 있습니다. 그 IP는 최근 스냅샷에 세션이 없지만 과거 관측은 있는 경우입니다. 요약 KPI의 최초/최종 관측과 연결 이력으로 과거 활동을 확인하세요(분포는 IP의 마지막 스냅샷 배치를 앵커로 복원합니다).

팝업의 "평판 ↗"가 화면 안이 아니라 새 탭으로 열립니다. 의도된 동작입니다. 이 트래픽 팝업은 외부 AbuseIPDB 링크를 씁니다. 신뢰도 게이지·신고 내역을 콘솔 안에서 보려면 보안 화면의 인앱 평판 팝업을 사용하세요.

강도 도트 색이 무슨 기준인가. 포트당 세션 수 버킷입니다 — 1–9 녹색 · 10–49 황색 · 50+ 적색 · 0이면 흐린 도트. 열은 감시 포트 앞 8개만 표시합니다.

관련 항목

IP 연결 — 서버별 원격 peer
IP 연결 — 서버별 원격 peer