← 목록
기타 2026-07-09 8KB 읽기 8분

#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 아님 — 파일 헤더 주석으로 확인). 두 가지 실버그를 규명:

  1. 줄분할목록() 통계트 분리 실패: 세미콜론도 {도 없이 공백 2칸 이상만으로
문장을 구분하는 이 코드베이스의 실제 관용구(예: 소켓닫기(소) 반환 거짓, 헤더끝위치 = lf위치 구분길이 = 2)를 인식하지 못해 조각 전체를 원문 그대로 통과시켰다 — RPN 후위형 위반(소켓닫기(소) 함수호출식 잔존)·bare = 등으로 발현.
  1. 정규화()의 예약어 오판: 반환 -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는 무편집.

    실배포 시 필요한 작업(적용 안 함 — 상위 오케스트레이터 판단)

    1. stunnel-live.conf/stunnel-live-443.conf의 각 서비스 섹션(또는 gateway 백엔드로
    가는 공통 섹션)에 protocol = proxy 추가 — stunnel_conf_gen.py(생성기) 수정이 맞는 지점(230개 섹션 수기 편집 대신).
    1. 게이트웨이통합.한선_요청처리/TCP accept 직후, HTTP 파싱 이전에 원시
    요청이 "PROXY "로 시작하는지 검사 → PROXY라인파싱()으로 클라IP 획득, PROXY라인제거()로 스트림에서 걷어낸 나머지를 이후 HTTP 파싱에 사용.
    1. GW_RATELIMIT=1 분기의 레이트리밋확인(...) 호출을
    레이트리밋확인IP(_rlmap,_rl상태,호스트,클라IP)로 교체.
    1. 게이트웨이정책코어_perip.한선을 검토 후 원본(pkg/libs/게이트웨이정책코어.한선)에
    병합(함수 추가만이라 기존 시그니처 무회귀).
    1. 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 수정+재기동, 게이트웨이통합.한선 배선, 정책코어
    원본 병합)는 미적용 — 지시대로 설계+그림자 프로토타입까지만.