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 は、誰も所有していない公開エンドポイントです。