Repository navigation
SSRF 내부 대역 정본과 서버 벽 블록 추가 - #138
Merged
Merged
Conversation
- contracts/internal-ranges.txt 하나에 내부 대역 목록을 둠 - 소비자: extractor 앱 검사·renderer 앱 검사·서버 벽 - 원인: 두 서비스가 각자 코드로 판정해 13개 주소 실측에서 3행 불일치 - IPv4 9개·IPv6 5개 - 198.18.0.0/15·192.0.2.0/24 처럼 인터넷에 라우팅되지 않을 뿐 우리 서버에서 닿을 곳이 없는 대역은 뺌 - IPv4 를 담은 IPv6 는 소비자가 IPv4 로 풀어 판정 - contracts/internal-ranges.test.sh 로 CI 에서 형식 검사 - 정규형·호스트 비트·겹침·IPv4 와 IPv6 각 1개 이상 - 원인: 소비자 셋이 각자 파싱해 한쪽만 받아들이는 줄이 생길 수 있음
- blocks/egress_wall.sh 가 컨테이너의 내부 대역 새 연결을 서버 iptables 에서 거절 - 원인: SG 는 허용 규칙만 있어 내부만 막고 인터넷을 허용하는 규칙을 못 만듦 - 원인: 브라우저의 service worker·SharedWorker·WebRTC 와 이름 해석으로 내부에 닿는 요청은 앱 검사가 못 봄 - 컨테이너가 서버 밖으로 가는 연결은 DOCKER-USER, 서버 자신으로 가는 연결은 INPUT 에서 막음 - 서버의 다른 주소(사설 IP)로 sshd·nginx 에 닿는 경로도 INPUT 에서 같이 막힘 - 모든 서버 공통 예외는 블록이 가짐: DNS·인스턴스 메타데이터 80·Alloy OTLP 4318 - DNS 는 Docker 가 컨테이너 resolv.conf 를 만드는 원본에서 읽음 - 서비스만 쓰는 목적지는 --allow-tcp 로 호출부가 넘김 - TCP 는 RST 로 거절 - 원인: ICMP 거절은 커널 제한(icmp_ratelimit 1000ms)에 묶여 연달아 막히면 SYN 재전송 1초를 기다림 (dind 실측 1.05초) - 재부팅 유지: 규칙 파일·적용 스크립트를 /etc 에 두고 systemd 단위가 Docker 보다 먼저 적용 - DOCKER-USER 를 블록이 통째로 가져 restore 가 비우고 다시 채움. 여러 번 돌려도 같음 - 적용 뒤 실행 중인 컨테이너의 네트워크에서 예외·차단 목적지를 찔러 거절 횟수로 확인 - 어긋나면 벽을 걷고 exit 1. 서비스는 벽 없이 계속 돌고 배포만 멈춤 - IPv6 가 켜진 bridge 가 있으면 적용하지 않음 - 원인: 벽은 IPv4 만 막음. 두 서버 실측은 IPv6 꺼짐 - 셀프 테스트는 운영과 같은 Docker 29.8.1 dind 안에 서버·VPC·인터넷을 흉내 내 실제 커널 규칙으로 확인
- RenderResponse.internal_address_hit 추가
- 브라우저가 내부 대역 주소에서 응답을 받으면 참. 이때 hops 는 비고 error 가 채워짐
- 판정은 이름이 아니라 브라우저가 실제로 연결한 IP 로 함
- 원인: extractor 가 이 경우를 일반 렌더 실패(일시)가 아니라 내부 주소(확정)로 구분해야 함
- 필드 추가라 어느 쪽이 먼저 배포돼도 안전 (모르는 쪽은 false·무시)
- extraction.proto 의 code 설명을 실제 의미에 맞춤
- BLOCKED_HOST: 렌더가 내부 대역 주소에서 응답을 받은 경우 포함
- MALFORMED_REDIRECT: https 가 아닌 위치 포함
- 이전: extractor 가 https 아닌 redirect 를 BLOCKED_HOST 로 냄. 내부 주소가 아닌데 SSRF 로 집계됨
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- 같은 bridge 안의 연결은 br_netfilter 가 켜진 커널에서만 iptables 를 지남
- 결과: CI 러너는 꺼져 있어 옆 컨테이너가 벽 밖, 로컬 Docker Desktop 은 켜져 있어 벽 안
- 두 운영 서버 실측: br_netfilter 꺼짐
- 시험은 커널 설정에 따라 기대값을 고름
- 블록 설명에 옆 컨테이너(같은 서비스의 다른 슬롯)도 앱 검사 몫임을 적음
- 대안: 블록이 br_netfilter 를 켜 같은 bridge 연결까지 막음
- 기각: 옆 컨테이너는 같은 서비스의 다른 슬롯뿐이라 자기 loopback 과 같은 표면. 모든 bridge 트래픽이 iptables 를 지나게 되는 비용만 생김
- internal_address_hit 를 optional bool 로 바꿈
- renderer 는 판정을 했으면 거짓도 늘 실어 보냄
- 필드가 없으면 판정하지 않는 renderer 라 호출자는 홉을 쓰지 않음
- 원인: extractor 가 홉 재검사를 지운 뒤 판정 없는 renderer(배포 순서가 어긋나거나 롤백된 경우)의 홉을 그대로 쓰면 SSRF 구멍이 열림 (독립 리뷰 지적)
- 대안: extractor 의 홉 재검사를 한 릴리스 더 둠
- 기각: 나중에 지울 코드를 단계 배포에 남김. 롤백 때는 다시 열림
- main 에 아직 없는 필드라 호환 깨짐 없음
- 적용 뒤 확인을 docker0 에 잠깐 붙인 시험용 netns 에서 함
- 그 출발 주소만 세는 카운터 규칙으로 거절 횟수를 판정
- 이전: 실행 중인 운영 컨테이너 네트워크에서 찌르고 서버 전체 거절 수를 봄
- 원인: 운영 트래픽이 그 사이 벽에 걸리면 정상 벽을 걷음. 컨테이너가 재시작 중이면 PID 0 으로 오판 (독립 리뷰 지적)
- 결과: 컨테이너가 없는 서버에서도 확인을 건너뛰지 않음
- 시험 주소는 Docker 가 앞에서부터 나눠 주는 bridge 대역의 끝쪽에서 빈 것을 고름
- --remove 추가
- 원인: 규칙 파일과 부팅 단위가 남아 배포 호출을 되돌려도 벽이 영구히 남음
- 실패 원복이 중간에 멈추던 결함 수정
- 원인: 시험 정리 함수의 마지막 줄이 set -e 에서 실패 상태로 끝남
- 시험 추가: 운영 컨테이너가 벽에 계속 걸리는 중의 적용, 시험 흔적 정리, --remove
- 음성 대조: DNS 예외를 뺀 사본은 확인에서 걸려 벽을 걷고 exit 1 (로컬 1회)
m-a-king
added a commit
to TeamPiKi/extractor
that referenced
this pull request
Oct 14, 2026
- 렌더 응답에 internal_address_hit 가 없으면 홉을 쓰지 않고 HEADLESS_UPSTREAM 일시 실패 - 원인: extractor 가 홉 재검사를 지웠음. renderer 가 판정 전 버전이면(배포 순서가 어긋나거나 롤백) 내부 응답이 그대로 들어옴 (독립 리뷰 지적) - 계약이 이 필드를 optional 로 바꿔 판정 여부를 실음 (TeamPiKi/infra#138) - 결과: 머지 순서를 코드가 지킴. renderer 가 먼저면 안전, extractor 가 먼저면 렌더만 일시 실패 - 음성 대조: 확인을 끄면 새 테스트 1건 실패
- install.sh 가 shared-infra/contracts 에 internal-ranges.txt 도 설치 - 원인: extractor 는 빌드가 classpath 에 싣고 renderer 는 기동 때 읽음. 없으면 둘 다 뜨지 않음 - 이전: proto 두 개만 설치. 로컬에서 renderer 테스트·extractor 기동 실패 - 설치 전 검사에 ranges 유형 추가 (첫 CIDR 줄 확인) - 결과: 오류 페이지·빈 본문은 설치하지 않음
- 규약 3절 egress 항목을 SG 기준으로 좁히고 서버 방화벽 항목 추가 - 막는 대상·거는 시점·공통 예외와 서비스 고유 예외의 주인 - 방화벽을 거치지 않는 곳은 각 서비스의 앱 검사 몫 - README 구조에 internal-ranges.txt·egress_wall.sh 추가
m-a-king
added a commit
to TeamPiKi/extractor
that referenced
this pull request
Oct 18, 2026
* fix: SSRF 검사를 직접 가져오기 한 곳으로 정리
- 내부 대역 판정을 infra contracts/internal-ranges.txt 로 바꿈
- 빌드가 정본을 classpath 에 실음. 없으면 가드를 만드는 빈 생성에서 기동 실패
- 이전: JDK 주소 분류 + CGNAT·ULA·메타데이터 개별 판정
- 결과: renderer 판정과 6개 대역에서 어긋남
- 렌더 호출 전의 첫 URL 재검사 삭제
- 원인: 렌더는 직접 가져오기가 실패한 뒤에만 탐. 같은 URL 이 이미 매 홉 검사를 통과함
- 렌더 홉 URL 의 재검사 삭제
- 이전: https 홉만 다시 이름 해석해 검사, http 홉은 건너뜀
- 원인: 렌더와 다른 시점의 이름 해석이라 브라우저가 실제로 간 IP 와 어긋날 수 있음
- renderer 가 실제 연결 IP 로 판정해 internal_address_hit 로 알리면 BLOCKED_HOST 로 번역
- https 아닌 redirect 를 MALFORMED_REDIRECT 로 내고 헤드리스로 승격
- 이전: BLOCKED_HOST. 내부 주소가 아닌데 SSRF 로 집계되고 승격도 막힘
- host 없는 주소는 INVALID_URL
- 이전: BLOCKED_HOST
- 판정을 끄는 대조: 정본에서 100.64.0.0/10 을 빼면 가드 테스트 2건 실패
* infra: extractor 배포에 서버 벽 적용
- 배포가 infra 의 egress_wall.sh 로 서버 벽을 건 뒤 새 슬롯을 띄움
- 이 서비스 고유 예외는 renderer 주소 하나
- 배포가 매번 계산하는 renderer 주소를 그대로 넘김. 포트·박스가 바뀌어도 같이 따라감
* fix: 내부 주소를 판정하지 않는 renderer 의 홉 거부
- 렌더 응답에 internal_address_hit 가 없으면 홉을 쓰지 않고 HEADLESS_UPSTREAM 일시 실패
- 원인: extractor 가 홉 재검사를 지웠음. renderer 가 판정 전 버전이면(배포 순서가 어긋나거나 롤백) 내부 응답이 그대로 들어옴 (독립 리뷰 지적)
- 계약이 이 필드를 optional 로 바꿔 판정 여부를 실음 (TeamPiKi/infra#138)
- 결과: 머지 순서를 코드가 지킴. renderer 가 먼저면 안전, extractor 가 먼저면 렌더만 일시 실패
- 음성 대조: 확인을 끄면 새 테스트 1건 실패
* fix: host 존재 검사를 링크 형식 검사로 이동
- ProductLink.parse 가 host 없는 주소를 INVALID_URL 로 거절
- 입구와 매 redirect 가 같은 함수를 거쳐 두 곳 모두 검사
- InternalHostGuard 의 host 없음 분기 삭제. 가드는 내부 대역 판정만 맡음
- host 없는 redirect 위치를 MALFORMED_REDIRECT 로 바꿈
- 이전: 다음 홉의 가드에서 걸려 INVALID_URL. 사용자 입력 오류로 잘못 분류됨
- 음성 대조: host 검사를 빼면 새 테스트 2건 실패
* fix: 매 홉 redirect 의 http 허용
- redirect 위치와 렌더 홉 주소가 http 도 받음
- 입력 링크는 그대로 https 만
- 원인: 내부 주소 판정과 IP 고정은 scheme 과 무관하게 매 홉에 걸림
- 원인: http redirect 를 거절해도 헤드리스로 넘어가 renderer 가 같은 홉을 따라감. 다운그레이드 차단 효과 없음
- 결과: 매 홉의 scheme 기준이 renderer 와 같아짐
- MALFORMED_REDIRECT 는 http·https 아닌 redirect 와 host 없는 redirect 위치만
- 렌더의 http 홉을 원본 link 로 폴백하지 않고 홉 주소로 씀
- finalUrl 이 http 일 수 있음
- 음성 대조: 홉도 https 만 받게 되돌리면 새 테스트 4건 실패
* docs: SSRF 단일 검사 구조로 문서 갱신
- style-decisions 의 헤드리스 redirect SSRF 비대칭 항목을 해소로 갱신
- final_url 사후 검증 삭제 이유
- 직접 가져오기는 보내기 전 IP 검사와 IP 고정
- renderer 는 보내기 전에 막지 못해 받은 뒤 결과를 버림
- style-decisions 의 ProductLink.HTTP_SCHEMES 잔가지를 소멸로 표시
- api-contract 에 render.proto·internal-ranges.txt 행 추가
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
한눈에
컨테이너가 사내망 주소로 연결하지 못하게 막는 서버 방화벽 규칙
Situation
원인 추가
Task
범위
Action
내부 대역 정본
100.64.0.0/10·224.0.0.0/40.0.0.0/8·240.0.0.0/4198.18.0.0/15·192.0.2.0/24서버 방화벽 블록
DOCKER-USER, 서버 자신으로 가는 새 연결은INPUT에서 거름INPUT에서 막힘--allow-tcp IP:PORT로 넘김icmp_ratelimit1000ms, 두 서버 실측)에 묶여 연달아 막히면 SYN 재전송을 기다림/etc에 두고 systemd 단위가 Docker 보다 먼저 적용DOCKER-USER를 블록이 통째로 가짐. 매 적용이 비우고 다시 채워 몇 번 돌려도 같음--remove로 벽과 부팅 단위를 걷음계약 필드와 code 설명
RenderResponse.internal_address_hit(optional): 브라우저가 내부 대역 주소에서 응답을 받으면 참BLOCKED_HOST·MALFORMED_REDIRECT설명을 실제 의미에 맞춤BLOCKED_HOST로 내 내부 주소가 아닌데 SSRF 로 집계됨로컬 계약 설치
install.sh가internal-ranges.txt도 소비 repo 의shared-infra/contracts/에 설치문서
conventions/infra.md3절: egress 항목을 보안 그룹 기준으로 좁히고 서버 방화벽 항목 추가README.md구조에internal-ranges.txt·egress_wall.sh추가Result
실제 커널 규칙으로 검증
--removeiptables-restore --test로 검사만 함(적용 0)systemd-analyze verify통과piki-wall:)는 dind 에서 볼 수 없어 운영 서버에서 처음 확인RequiredBy=docker.service후속
연관 이슈