서문
역방향 프록시를 통해 노출된 자체 호스팅 서비스는 잘못된 구성, 관리자 패널, 취약한 엔드포인트를 탐색하는 자동화된 스캐너를 필연적으로 끌어들일 수밖에 없습니다. 이 문서에서는 Elastic Security와 Cloudflare를 사용하여 일상적인 Traefik 액세스 로그를 능동적 방어 제어로 전환하는 방법을 보여드립니다.
저는 기본 제공 ES|QL 탐지 규칙을 사용하여 웹 서버 검색 및 퍼징 동작을 식별합니다. 의심스러운 프로빙 패턴이 감지되면 자동화된 워크플로우가 Cloudflare API를 통해 에지에서 문제가 되는 소스 IP를 즉시 차단합니다. 이 설정의 가장 좋은 점은 손쉽게 확장할 수 있다는 점입니다. 퍼징 탐지를 위해 이 응답 배관을 한 번 구축하면 SQL 주입이나 파일 포함 시도를 포착하는 것과 같은 다른 모든 Elastic 규칙에 정확히 동일한 차단 조치를 첨부할 수 있습니다. 이를 통해 기본 로깅 파이프라인을 고도로 적응 가능한 경계 방어 시스템으로 전환할 수 있습니다.
배경 및 위협 환경
제 홈랩 설정은 컨테이너 및 가상 머신에 Proxmox VE를 활용합니다. 인증을 위해 Authelia로 보안된 Traefik 역방향 프록시를 사용하여 VPN 없이 외부 액세스를 허용합니다. 프록시가 활성화된 Cloudflare는 DNS를 관리합니다.
이 특정 스택에 익숙하지 않은 분들을 위해 Traefik은 네트워크의 정문 역할을 합니다. 웹 요청이 Cloudflare를 통해 도착하면 Traefik은 트래픽을 올바른 내부 컨테이너로 동적으로 라우팅하는 동시에 SSL 인증서를 관리하여 연결을 암호화 상태로 유지합니다. 그러나 트래픽이 실제로 이러한 백엔드 애플리케이션에 도달하기 전에 Authelia가 가로채게 됩니다. Authelia는 Traefik의 포워드 인증 기능을 활용하여 싱글 사인온 및 다단계 인증을 전반적으로 시행합니다. 즉, 자동화된 스캐너와 공격자는 초기 보안 포털을 통과하지 않고는 내부 서비스의 로그인 화면에도 접근할 수 없습니다.
가시성과 보안을 유지하기 위해, 저는 공식 통합을 사용해 이러한 Traefik 액세스 로그를 Elastic으로 수집합니다. 일상적인 모니터링 중에 이러한 로그에서 동일한 소스 IP 주소에서 시작된 수많은 HTTP 404 응답 코드를 관찰했습니다.
이 패턴은 내 네트워크에서 실제로 사용되지 않는 애플리케이션의 취약점을 노리는 잠재적인 웹 서버 프로빙 또는 퍼징 트래픽을 시사합니다. 이러한 타겟 경로의 예로는 /wp-includes/mani., /wp-content/plugins/all-in-one-wp-security-and-firewall/templates.php, /archive.php, /wp-admin/includes/header.php 등이 있습니다.
디자인 철학: 왜 Fail2Ban이 아닌가요?
홈랩 커뮤니티에서 자주 묻는 질문은 왜 Traefik 서버에서 직접 Fail2Ban이나 CrowdSec과 같은 로컬 도구를 사용하지 않느냐는 것입니다. 이러한 도구도 훌륭하지만, Elastic Security를 통해 대응을 오케스트레이션하고 Cloudflare로 블록을 푸시하면 두 가지 주요 이점이 있습니다. Cloudflare 에지에서 악성 트래픽을 차단하면 로컬 대역폭을 절약하고 스캐너를 홈 네트워크에서 완전히 차단할 수 있습니다. 또한 Elastic을 통해 대응을 오케스트레이션하면 모든 보안 모니터링을 위한 단일 창을 확보할 수 있습니다.
탐지 전략 및 구현 전략
악의적인 정찰을 효과적으로 식별하기 위해 프록시 수준에서 HTTP 응답 코드의 빈도를 분석하는 전략에 의존합니다. 특히 디렉터리 퍼징 또는 취약점 스캔의 대표적인 지표인 단일 소스 IP에서 짧은 시간 내에 대량의 404 (찾을 수 없음) 오류가 발생하는지 살펴보고 있습니다.
Elastic Security는 이 정확한 시나리오를 위한 강력한 기본 제공 탐지 규칙을 제공하지만, 이러한 규칙이 올바르게 작동하려면 적절하게 정규화된 ECS(Elastic Common Schema) 데이터가 필요합니다. 따라서 이러한 스캔을 탐지하고 완화하려면 조율된 흐름이 필요합니다. 이를 작동시키려면 Traefik 로그를 수집하고, 사용자 정의 파이프라인을 사용하여 누락된 host.name 필드를 패치한 다음, 탐지 규칙이 데이터를 가리키도록 해야 합니다.
임계값 로직 및 튜닝
저희의 탐지 전략은 단순한 문자열 매칭에서 벗어나 통계적 임계값에 의존합니다. 이 규칙은 특히 HTTP 403 및 404 응답 코드로 표시되는 거부되거나 존재하지 않는 리소스를 모니터링하고 원본 소스 IP별로 이 활동을 집계합니다.
이 동작은 쿼리의 마지막 where 문에 의해 제어됩니다. 기본적으로 알림은 폴링 기간 동안 소스 IP가 250 고유 URI 경로에서 500 오류를 2개 이상 생성하는 경우에만 트리거됩니다. 이 이중 계층 임계값은 오탐을 제거하도록 설계되어 하나의 손상된 자산으로 인해 차단이 트리거되지 않도록 하면서도 디렉토리 단어 목록을 순환하는 자동화된 스크립트를 식별할 수 있습니다.
소규모 홈랩이나 소규모 팀 환경에서는 이러한 기본값이 너무 관대한 경우가 많습니다. 합법적인 외부 트래픽은 내 네트워크에 존재하지 않는 관리자 패널을 공격할 이유가 없으므로 은밀한 정찰 활동을 조기에 포착할 수 있도록 감도를 조정했습니다. event_count > 100 과 url_original_count_distinct > 50 이 트리거되도록 로직을 수정했습니다.
애플리케이션이 자연스럽게 더 많은 오류를 생성하는 프로덕션 환경의 경우 이 값을 늘리거나 ES|QL where not 절을 추가하여 알려진 끊어진 링크를 제외하는 것을 고려할 수 있습니다. 마지막으로 where source.ip not in (...) 필터를 사용하여 인증된 보안 도구나 개인 취약성 스캐너가 자동화된 워크플로에 의해 실수로 금지되지 않도록 합니다.
Traefik 액세스 로그 수집
Traefik 액세스 로그를 클러스터로 수집하기 위해 Traefik의 기본 통합을 사용했습니다. Elastic 에이전트는 Traefik 서버에서 로그를 수집합니다. 이 통합은 수집된 로그를 logs-traefik.access-default 데이터스트림에 기록합니다.
사용자 정의 수집 파이프라인 구축
host.name 필드는 제가 사용 중인 탐지 규칙에 매우 중요하지만 기본 Traefik 연동에서는 이 필드가 채워지지 않습니다. 따라서 이 필드를 추가하려면 사용자 정의 수집 파이프라인이 필요합니다. Traefik 통합은 Traefik 서버의 파일 스트림을 활용하므로 기존 agent.name 필드에서 값을 복사하여 host.name 을 채울 수 있습니다.
저는 특히 메인 파이프라인을 수정하는 대신 logs-traefik.access@custom 파이프라인을 사용합니다. Elastic 통합은 처리 흐름이 끝날 때 바로 이러한 @custom 파이프라인을 자동으로 선택해 실행하도록 설계되었습니다. 더 중요한 것은 통합을 업그레이드할 때마다 기본 파이프라인이 완전히 덮어씌워진다는 점입니다. 사용자 지정 파이프라인에 로직을 숨기면 다음 업데이트에서도 필드 매핑이 실제로 유지됩니다. 이 파이프라인을 만드는 데 필요한 API 호출은 개발자 도구 콘솔에서 실행할 수 있습니다:
PUT _ingest/pipeline/logs-traefik.access@custom
{
"description": "copy the agent.name field to the host.name field",
"processors": [
{
"set": {
"field": "host.name",
"value": "{{{agent.name}}}",
"override": false,
"ignore_empty_value": true,
"ignore_failure": true
}
}
]
}
Cloudflare 워크플로우를 통한 자동화된 응답
탐지에서 능동적 방어로 전환하기 위해, Elastic 경보와 Cloudflare 에지 간의 격차를 해소하는 워크플로우를 구현합니다. 이 로직은 효율적으로 설계되었습니다. 모든 알림에 대해 새로운 방화벽 규칙을 생성하는 대신, 워크플로에서 먼저 기존 차단 목록을 검색하여 Cloudflare의 규칙 제한에 빠르게 도달합니다. 그런 다음 해당 목록에 새로운 위반 소스 IP를 동적으로 추가한 후 업데이트를 Cloudflare API에 다시 푸시합니다. 엣지가 보안이 확보되면, 워크플로는 Elastic에서 경보를 승인함으로써 인시던트에 대한 루프를 효과적으로 닫는 것으로 마무리됩니다.
전제 조건 및 토큰 범위
이 프로세스에는 Cloudflare 구성을 위한 API 키와 영역 ID가 모두 필요합니다. 규칙을 만들려면 API 토큰에 "영역 WAF 편집" 권한이 있어야 합니다. Cloudflare 대시보드에서 이 토큰을 생성할 때 "사용자 지정 토큰 만들기" 옵션을 사용하고 권한을 로 엄격하게 Zone -> Zone WAF -> Edit 설정합니다.
워크플로우가 구성되면 "웹 서버 검색 또는 퍼징 활동" 탐지 규칙에 작업으로 할당해야 합니다.
전제 조건이 갖추어졌으니 이제 워크플로를 구축하는 방법을 단계별로 살펴보겠습니다.
워크플로 구성 및 트리거
먼저 기본 메타데이터를 정의합니다. 이 워크플로에서는 웹 서버 검색 또는 퍼징 활동의 알림에서 발견된 IP 주소를 차단합니다. 워크플로가 활성화되어 있으며 API 요청에 대한 시간 제한은 30 초입니다. 이 경우 알림을 기반으로 하므로 보안 알림이 트리거되면 자동으로 실행됩니다.
# =========================================================================
# Workflow: Block IP at Cloudflare test
# Category: security/response
# =========================================================================
version: '1'
name: Block IP at Cloudflare
enabled: true
triggers:
- type: alert
상수 및 인증
이 섹션에는 인증을 위한 변수가 들어 있습니다. 플레이스홀더 문자열을 실제 API 토큰과 영역 ID로 대체하는 것을 잊지 마세요.
consts:
cloudflare_api: "<cloudflare API>"
cloudflare_zone: "<cloudflare ZONE>"
1단계: 현재 차단 목록 검색하기
이 시퀀스는 방화벽 규칙이 이미 존재하는지 확인합니다. 워크플로에서는 기존 IP 차단 규칙을 검색하기 위해 HTTP GET 요청을 합니다.
steps:
- name: cloudflare_current_block
type: http
with:
url: "https://api.cloudflare.com/client/v4/zones/{{consts.cloudflare_zone}}/rulesets/phases/http_request_firewall_custom/entrypoint"
headers:
Authorization: Bearer {{consts.cloudflare_api}}
method: GET
on-failure:
continue: true
2단계: 방화벽 규칙 업데이트 또는 만들기
존재하는 경우 규칙에 IP 주소가 추가되고, 그렇지 않으면 규칙이 만들어집니다. 워크플로에서는 "웹서버 검색 블록" 설명이 있는지 확인합니다. 그렇다면 PUT 요청을 통해 차단된 IP 주소의 현재 목록에 새 IP 주소를 추가합니다. 그렇지 않은 경우 새 규칙을 만드는 것으로 돌아갑니다.
- name: cloudflare_block
type: if
condition: 'steps.cloudflare_current_block.output.data.result.rules[0].description == "webserver scanning block"'
steps:
- name: ip-block-cloudflare_add
type: http
with:
url: "https://api.cloudflare.com/client/v4/zones/{{consts.cloudflare_zone}}/rulesets/phases/http_request_firewall_custom/entrypoint"
method: PUT
headers:
Authorization: Bearer {{consts.cloudflare_api}}
timeout: 30s
body: '{ "rules": [ { "description": "webserver scanning block", "expression": "{{steps.cloudflare_current_block.output.data.result.rules[0].expression}} or (ip.src eq {{event.alerts[0].source.ip}})", "action": "block" } ]}'
else:
- name: ip-block-cloudflare_new
type: http
with:
url: "https://api.cloudflare.com/client/v4/zones/{{consts.cloudflare_zone}}/rulesets/phases/http_request_firewall_custom/entrypoint"
method: PUT
headers:
Authorization: Bearer {{consts.cloudflare_api}}
timeout: 30s
body: '{ "rules":[ { "description": "webserver scanning block", "expression": "(ip.src eq {{event.alerts[0].source.ip}})", "action": "block" } ]}'
on-failure:
continue: true
3단계: 알림 승인하기
그러면 알림이 승인됩니다. 이 단계에서는 kibana.SetAlertsStatus 작업을 사용하여 Elastic Security에서 경보를 자동으로 종료합니다.
- name: update_alert_status
type: kibana.SetAlertsStatus
with:
status: "acknowledged"
signal_ids: ["{{event.alerts[0]._id}}"]
4단계: 워크플로를 규칙에 연결하기
워크플로우가 완전히 구축되었으면 마지막 단계는 실제로 탐지 규칙에 첨부하여 자동으로 실행되도록 하는 것입니다. "웹 서버 검색 또는 퍼징 활동" 규칙에 대한 Elastic Security 규칙 설정에서 규칙 작업 탭으로 이동하여 새 작업을 추가합니다. 커넥터 드롭다운에서 방금 만든 Cloudflare 워크플로우를 선택하기만 하면 됩니다.
WAF 제한에 대한 참고 사항
이 워크플로에서는 or 문(or (ip.src eq <IP>))을 사용하여 IP 주소를 연결하므로, Cloudflare는 사용자 지정 WAF 표현식에 대해 글자 수 제한이 있습니다(일반적으로 표준 계층에서는 4096 글자 수). 고도로 타겟팅된 환경에서는 이 문자열이 결국 한계에 도달할 수 있습니다. 홈랩이나 소규모 팀의 경우 가끔씩 이 WAF 규칙을 수동으로 지우는 것이 건강한 초기화 역할을 합니다.
테스트 및 유효성 검사
파이프라인이 엔드 투 엔드 방식으로 작동하는지 확인하기 위해 표준 퍼징 도구를 사용하여 약간의 노이즈를 생성할 수 있습니다. ffuf 또는 gobuster 과 같은 퍼징 도구를 사용하여 자체 홈랩에 대한 스캐닝 공격을 시뮬레이션할 수 있습니다.
공개용 Traefik 도메인에 존재하지 않는 디렉터리에 대해 빠른 검사를 실행하세요:
ffuf -u https://your-domain.com/FUZZ -w /path/to/wordlist.txt
시뮬레이션이 실행되면 자동화된 방어 체인이 작동하는 것을 관찰할 수 있습니다. 404 오류는 logs-traefik.access-default 데이터스트림에 거의 즉시 나타납니다. 폴링 간격 내에서 ES|QL 규칙은 패턴을 식별하고 Elastic 보안 경보 페이지에서 새 경보를 생성합니다. 거기서부터 워크플로가 이어집니다. 경보 상태를 "확인됨" 으로 변경하고 IP 차단을 Cloudflare WAF 규칙으로 푸시하여 스캐너가 정찰을 계속하기 전에 엣지에서 스캐너를 효과적으로 무력화합니다.
Security -> WAF -> Custom rules 에서 Cloudflare 대시보드를 확인하여 차단이 성공했는지 확인할 수 있습니다. (참고: 나중에 Cloudflare 규칙에서 IP를 제거하여 스스로를 잠그지 않도록 하세요!)
방어 확장
이 설정의 장점은 Cloudflare 워크플로우가 퍼징 감지에만 국한되지 않는다는 점입니다. 자동화가 구축되면 의심스러운 프록시 트래픽에 플래그를 지정하는 모든 Elastic 규칙에 자동화를 연결할 수 있습니다. 예를 들어 웹 서버 로컬 파일 포함 활동, 웹 서버 잠재적 원격 파일 포함 활동과 같은 특정 애플리케이션 익스플로잇을 대상으로 하는 기본 제공 규칙에 이와 동일한 대응 조치를 연결하여 공격자를 즉시 차단할 수 있습니다. 또한 웹 서버 오류 로그의 잠재적 급증 및 비정상적인 웹 사용자 에이전트와 완벽하게 결합하여 잘못 구성된 스크레이퍼와 광범위한 네트워크 노이즈를 포착합니다. 배관 공사를 한 번 하면 갑자기 주변이 모두 스마트해집니다.
결론
Traefik과 Cloudflare를 Elastic Security에 연결하면 기본 액세스 로그를 능동적인 방어 수단으로 전환할 수 있습니다. 홈랩 환경은 자동화된 스캐너가 끊임없이 쏟아져 들어와 낮은 곳에 매달린 과일을 찾습니다. 이 자동화된 워크플로우는 엣지에서 공격자를 차단할 뿐만 아니라 인시던트를 자동으로 인식하여 경보 피로도도 줄여줍니다. 보안 오케스트레이션 및 대응을 통해 보안 태세를 크게 개선하면서 시간을 절약하는 방법을 보여주는 실질적인 예입니다.