#15: RPN 변환기 출력정합 + RateLimit per-IP 설계/프로토타입
개요
2파트 작업. (A) 게이트웨이 RPN 정본 미인식 77건의 근본원인을 clike_to_rpn.한선(추출기)
자체 버그로 규명·수정. (B) 도메인단위 RateLimit을 per-IP로 전환하기 위한 설계+그림자
프로토타입(라이브 무접촉).
파트A: RPN 변환기 출력정합
근본원인 (clike_to_rpn.한선)
/Users/ef/CrownyOS/crownyc/tools/게이트웨이Q/게이트웨이통합.rpn.한선은
tools/clike_to_rpn.한선(hanseonc_high로 컴파일 후
CROWNY_ARG1=원본 crownyc run으로
추출)로 생성된다(high2rpn.sh 아님 — 파일 헤더 주석으로 확인). 두 가지 실버그를 규명:
- 줄분할목록() 통계트 분리 실패: 세미콜론도
{도 없이 공백 2칸 이상만으로
문장을 구분하는 이 코드베이스의 실제 관용구(예:
소켓닫기(소) 반환 거짓,
헤더끝위치 = lf위치 구분길이 = 2)를 인식하지 못해 조각 전체를 원문 그대로
통과시켰다 — RPN 후위형 위반(
소켓닫기(소) 함수호출식 잔존)·bare
= 등으로 발현.
- 정규화()의 예약어 오판:
반환 -1처럼 예약어(반환/그리고/또는/동안 등) 바로
뒤에 오는 단항 부호(
-)를, 예약어의 마지막 한글 음절이 "식별자 문자"라는
이유만으로 이항 연산자로 오판 →
- 1 두 토큰으로 쪼개짐(bare
-).
수정
함수 이중공백찾기(t): 깊이0(문자열/괄호/대괄호/중괄호 밖)의 공백2칸+ 위치 탐지.
줄분할목록()의 최종 폴백(
{없음)에서 이 위치로 재귀 분할.
함수 마지막단어(s)/함수 예약어인가(w): 트림된 출력 끝의 "단어"가 예약어 목록에
있으면
피연산자끝() 판정을 무시하고 단항 부호로 처리.
검증
- golden_harness.sh(tests/*.한선 87개) 무회귀. REGRESS(direct) 5건은 추출기 미참조
경로(수정과 무관, 환경/타이밍성). REGRESS(chain) 6건은 전부 "수정 전 골든도 이미
chain!=direct(손실 상태)"로 사전확인 — 오히려 diff 축소(예: 벡터729라이브러리테스트
chain이 golden.direct와 trailing-newline 제외 완전 일치로 개선).
실질 회귀 0건.
- 게이트웨이통합.한선(2026-07-09 최신) 재생성 → hanseonc_rpn 재컴파일:
구버전 97건 → 신버전 74건(-23), 이번에 타깃한 3개 패턴(bare
- 15건, bare
=
2건,
소켓닫기(소)류 함수호출식잔존 2건)
전부 0건으로 완전 해소.
- 잔여 74건은 3개 그룹으로 분류(신규 rpn.한선 헤더에 상세 기록):
(a) 변수 로드/스코프 순서 의심 + 2단계 중첩 함수호출식(
숫자변환(꺼내(m,k))) 미지원,
(b) 단일라인
만약{} 뒤 별도줄
아니면{가 오면 블록스택이 조기 pop되는 lookahead 부재
구조적 한계(파일변환()의 라인드라이버 리팩터 필요, 이번 범위 밖),
(c) hanseonc_rpn 키워드테이블 미이식 잔존 가능성.
관련 파일
/Users/ef/CrownyOS/crownyc/tools/clike_to_rpn.한선 — 수정(원본)
/Users/ef/CrownyOS/crownyc/tools/clike_to_rpn.한선.bak-20260709-rpn정합 — 수정 전 백업
/Users/ef/crowny-gateway/게이트웨이Q/게이트웨이통합.rpn.한선 — 재생성(신규 헤더로 근거 기록)
/Users/ef/crowny-gateway/게이트웨이Q/게이트웨이통합.rpn.toau — 재컴파일
*.bak-20260709-preRPN정합 — 양쪽 수정 전 백업
파트B: RateLimit per-IP 설계/프로토타입 (라이브 무접촉)
현황
게이트웨이통합.한선은
GW_RATELIMIT=1일 때만
레이트리밋확인(_rlmap,_rl상태,호스트)을
호출(기본 OFF — 도메인단위 강제가 저한도 서비스에서 정상 트래픽까지 429시켜 회귀
실측됨). 함수 정의는
/Users/ef/CrownyOS/crownyc/pkg/libs/게이트웨이정책코어.한선
(라이브 컴파일 그래프에 포함되는 실 라이브러리).
실측 발견: xforwardedfor 옵션 부재 → protocol=proxy로 전환
이 배포환경의
stunnel 5.78(homebrew)은
xforwardedfor 옵션이 아예 없다
(
stunnel -help 전체 95개 옵션에 부재, config에 넣으면 즉시
"Specified option name is not valid here"로 설정실패 — 그림자 8095 포트에서
직접 확인). 대안으로 서비스 섹션에
protocol = proxy를 추가해 그림자
stunnel(8095→8096, 라이브 stunnel-live.conf 무접촉,
/tmp/stunnel-shadow-xff.conf
사본)을 실제로 띄우고
openssl s_client로 실 HTTPS 요청을 흘려보낸 결과, 백엔드
(
nc -l 8096으로 캡처)가 실제로 받은 바이트:
PROXY TCP4 127.0.0.1 127.0.0.1 49458 8095
GET / HTTP/1.1
Host: xfftest.local
Connection: close
→ stunnel이 HAProxy
PROXY protocol v1 줄을 실제 클라 IP로 백엔드에 진짜 삽입함을
실측 확인.
결론: 이 배포환경의 실배포 경로는 X-Forwarded-For 헤더가 아니라
PROXY protocol v1 줄 파싱이어야 한다.그림자 구현물 (전부 라이브 무접촉, 게이트웨이Q/그림자/)
게이트웨이정책코어_perip.한선 — /Users/ef/CrownyOS/crownyc/pkg/libs/게이트웨이정책코어.한선의
사본(원본 무편집). 추가 함수:
_레이트리밋판정(rl맵,rl상태,도메인,키): 기존 로직을 "키" 파라미터로 일반화(내부).
레이트리밋확인(rl맵,rl상태,도메인): 기존 시그니처·동작 완전 불변(키=도메인 위임).
레이트리밋확인IP(rl맵,rl상태,도메인,클라IP): 키=도메인+"|"+클라IP
(클라IP 빈 문자열이면 도메인단위로 자동 폴백).
PROXY라인파싱(요청원문) / PROXY라인제거(요청원문): PROXY protocol v1 줄 파싱/제거
(실배포 경로).
클라IP추출(요청원문): X-Forwarded-For 파싱(다른 배포 대상에서 xforwardedfor가
지원되는 경우를 위해 보조로 남김).
perip_레이트리밋_실증.한선 — 컴파일+실행 검증 테스트(21개 PASS 전부):
클라IP추출 3종, per-IP 카운터 완전 격리(IP-A 429여도 IP-B 통과, rl상태 슬롯이
IP당 3칸으로 정확히 분리 —
길이(rl상태)==6 실측), 기존 레이트리밋확인() 무회귀,
클라IP="" 폴백, PROXY라인 파싱/제거(그림자 stunnel 실측 캡처값 그대로 재사용),
종단간(PROXY라인파싱→레이트리밋확인IP) 시뮬레이션.
stunnel-shadow-xff.conf.실측검증됨 — 검증에 쓴 그림자 stunnel 설정(8095→8096,
protocol = proxy). 라이브
stunnel-live.conf는 무편집.
실배포 시 필요한 작업(적용 안 함 — 상위 오케스트레이터 판단)
stunnel-live.conf/stunnel-live-443.conf의 각 서비스 섹션(또는 gateway 백엔드로
가는 공통 섹션)에
protocol = proxy 추가 —
stunnel_conf_gen.py(생성기) 수정이
맞는 지점(230개 섹션 수기 편집 대신).
게이트웨이통합.한선의 _요청처리/TCP accept 직후, HTTP 파싱 이전에 원시
요청이
"PROXY "로 시작하는지 검사 →
PROXY라인파싱()으로 클라IP 획득,
PROXY라인제거()로 스트림에서 걷어낸 나머지를 이후 HTTP 파싱에 사용.
GW_RATELIMIT=1 분기의 레이트리밋확인(...) 호출을
레이트리밋확인IP(_rlmap,_rl상태,호스트,클라IP)로 교체.
게이트웨이정책코어_perip.한선을 검토 후 원본(pkg/libs/게이트웨이정책코어.한선)에
병합(함수 추가만이라 기존 시그니처 무회귀).
- stunnel 설정 변경은 재기동 필요(라이브 영향) — 오케스트레이터가 유지보수
윈도우 결정.
검증 요약
- 그림자 stunnel 8095/nc 8096 프로세스 전부 종료 확인, 라이브 포트(8080/8443/9100/9152)·
라이브 stunnel 프로세스(pid 25865/7349) 무변동 확인.
perip_레이트리밋_실증.한선 21/21 PASS(hanseonc_high 컴파일 + crownyc 실행, 실측
stunnel 캡처값 재사용).
잔여 이슈 (정직 보고)
- 파트A: 74건 미인식 잔존(3그룹 분류, 위 참조) — (b) 구조적 lookahead 한계는 별도
세션 필요.
- 파트B: 실배포(stunnel-live.conf 수정+재기동, 게이트웨이통합.한선 배선, 정책코어
원본 병합)는 미적용 — 지시대로 설계+그림자 프로토타입까지만.