VPN 뒤에 있는 Jenkins에 대해 GitHub webhook이 시간 초과되는 이유
GitHub는 webhook을 안쪽으로 보내므로 Jenkins를 보호하는 VPN이 이를 거부합니다. 전달을 relay로 바깥으로 내보내거나, webhook을 폴링하는 에이전트로 바꾸세요.
전달 페이지에는 시간 초과가 표시되고 Jenkins에는 아무것도 기록되지 않는데, 이것은 한 번도 도착하지 않은 연결의 특징입니다. GitHub는 webhook을 자체 인프라에서 구성한 payload URL로 향하는 아웃바운드 HTTP POST로 보냅니다. VPN은 바로 그런 종류의 연결을 거부함으로써 Jenkins를 보호합니다. 어느 쪽도 고장 나지 않았습니다. 문제는 요청의 방향입니다.
전달이 실제로 시도하는 것
GitHub는 payload URL의 호스트 이름을 확인하고 공용 인터넷에서 거기로 연결합니다. 그 URL이 사설 주소를 가리키거나 네트워크 내부에서만 확인되는 이름을 가리키면 요청이 전송되기 전에 시도가 실패합니다. 엣지가 버리는 공용 이름을 가리키면 대신 시도가 시간 초과됩니다.
이 구분은 진단에 중요합니다. DNS 실패와 필터링된 포트는 둘 다 Jenkins 쪽에서 아무 일도 없었던 것처럼 보이며, 둘 다 GitHub에는 다른 이유가 붙은 실패한 전달로 나타납니다. URL을 바꾸기 전에 그 이유를 읽으세요.
바깥으로 연결하는 터널은 리스너를 만들지 않습니다. 사람들이 다르게 작동할 것이라고 기대하는 부분이 바로 이것입니다.
연결 방향을 고치는 세 가지 방법
모든 해결책은 연결을 누가 시작하는지를 바꾸며, 형태는 세 가지뿐입니다.
| Approach | Who initiates | What you give up |
|---|---|---|
| Relay service | An outbound client inside the network | A third party sees every payload |
| Reverse tunnel or ingress | The public edge, into a controlled listener | An inbound surface you must secure |
| Runner or polling | The agent, outbound to GitHub | Instant delivery, replaced by a schedule |
폴링은 가장 세련되지 않았지만 운영하기 가장 쉽습니다. relay는 포트를 열지 않고 거의 즉각적인 피드백을 유지합니다. payload 기밀성이 삼자를 배제할 때는 작업이 이미 네트워크 안에 있는 머신에서 실행될 수 있으므로 runner가 보통 정답입니다.
운영할 수 있는 것을 기준으로 고르고, 현대적으로 들리는 것을 기준으로 고르지 마세요.
smee로 전달을 바깥으로 내보내기
relay 서비스에서 채널을 만들고 그 URL을 보관합니다. 네트워크 안에서 클라이언트를 설치하고 Jenkins가 기대하는 경로로 전달을 보냅니다. 저장소 설정의 payload URL이 채널 URL이 되고, 콘텐츠 유형은 application/json으로 설정됩니다.
sudo npm install --global smee-client
smee -u CHANNEL_URL --path /github-webhook/ --port 8080세 가지 세부 사항이 작동 여부를 결정합니다. 경로는 Jenkins가 GitHub 이벤트에 기대하는 것과 일치해야 하며 기본값은 /github-webhook/입니다. Jenkins는 클라이언트에 준 포트에서 수신해야 합니다. 그리고 작업에 구성된 Jenkins URL이 그 경로와 일치해야 하며, 그렇지 않으면 이벤트가 도착한 뒤 버려집니다.
끝의 슬래시가 일치하지 않으면 404가 생기며, 이는 전달 실패처럼 읽히지만 실패가 아닙니다.
일주일을 버티도록 실행하기
포그라운드 클라이언트는 터미널과 함께 죽고, webhook은 하루 동안 작동하다 멈춥니다. Restart=always가 있는 unit을 주고, Jenkins 사용자로 실행하고, 첫 실제 전달 후 journal을 읽으세요. 마지막 이 단계가 작동하는 relay와 구성된 relay를 구분합니다.
[Unit]
Description=smee webhook relay
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=2
User=jenkins
ExecStart=/usr/bin/smee -u CHANNEL_URL --path /github-webhook/ --port 8080
[Install]
WantedBy=multi-user.target재생 버튼 대신 실제 push로 테스트하세요. 재생은 relay가 저장한 payload를 실행할 뿐 GitHub에서 채널까지의 경로를 실행하지 않으므로, 라이브 전달이 여전히 실패해도 통과할 수 있습니다.
unit이 멈추면 경고하세요. 내려간 relay는 구조상 조용하며, 그 조용함이 바로 여러분이 이미 디버깅하는 대상입니다.
relay가 볼 수 있는 것, 그리고 쓰지 말아야 할 때
relay는 payload를 종료하므로 저장소 이름, commit 데이터, 이벤트가 운반하는 모든 것을 봅니다. 채널 URL을 공개 엔드포인트로 취급하세요. 위조된 전달이 거부되도록 Jenkins secret token을 구성된 상태로 유지하고, Jenkins가 사용하는 token의 범위를 필요한 최소로 제한하세요.
payload 기밀성이 그것을 배제한다면, 자체 호스팅 runner 또는 인증된 리버스 터널이 답입니다.
relay는 이제 인프라이며, 그것이 트리거하는 build와 같은 검토를 받을 자격이 있습니다. 채널이 유출되면 교체하고, 마지막으로 라이브 전달을 검증한 날짜를 기록하세요. 그 날짜를 채널 URL 옆 runbook에 적습니다. 일 년 동안 아무도 검토하지 않은 relay는 아무도 소유하지 않은 공개 엔드포인트입니다.