how to create a reverse proxy with haproxy

概要

本当のオリジンIPを隠し、パフォーマンスを向上させ、サードパーティからアプリを保護するために、VPS上でHAProxyを使用してリバースプロキシを設定する方法について説明します。本当のオリジンサーバIPを隠すためにHAProxyでリバースプロキシを作成する方法


リバースプロキシとは何ですか?

リバースプロキシはクライアントとサーバーの間に位置します。リクエストを受信し、送信先を決定し、レスポンスを返します。また、複数のバックエンド間の負荷分散、セキュリティヘッダーの追加、不正なクライアントのレート制限、TLS (HTTPS) 終端の一元管理も可能である。

リバースプロキシは

スマートな用心棒のようなものだと考えてほしい。

つまり、リバースプロキシは、オリジンがプライベートに保たれている間、すべてをスムーズに動作させる静かな働き者なのである。


HAProxyによるリバースプロキシ:どのように動作するか

HAProxyは強力なL4/L7プロキシとロードバランサです。クライアントのリクエストはまずHAProxyにヒットします:

  • TLS (HTTPS) を終了することができる。
  • X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-Hostのようなヘッダが追加される。
  • トラフィックは、ホスト名、パス、またはカスタムルールによってバックエンドにルーティングされる。
  • ヘルスチェック、自動フェイルオーバー、レート制限、圧縮、軽いキャッシュ、WebSocket、gRPCパススルーが利用可能です。
  • 詳細なログとライブ統計ページが観測可能性を提供します。

結論:HAProxyはアーキテクチャを簡素化し、セキュリティとパフォーマンスを向上させ、スケーリングを容易にする。


HAProxyの長所と短所

長所(HAProxyが輝く理由)

  • 高いパフォーマンスと低いオーバーヘッド(イベントドリブン、マルチスレッド)。
  • L4 + L7のスマートさ(TCP/SNIパススルーまたは完全なHTTPルーティング/リライト)。
  • 堅牢なロードバランシングとヘルスチェック(ラウンドロビン、leastconn、ハッシュ、アクティブチェック、フェイルオーバー)。
  • セキュリティ機能(TLS終端、HSTS、ACL、スティックテーブルによるレート制限、IP許可/拒否)。
  • 観測可能性(豊富なログ、ライブ統計ソケット/ページ、Prometheusエクスポータが利用可能)。
  • 信頼性(ダウンタイムがほぼゼロに近いグレースフル・リロード。)
  • フットプリントが小さい(Linux/BSD/コンテナなど、事実上どこでも動作)。

短所(トレードオフ)

  • 学習曲線(強力だが冗長な設定)。
  • 証明書の自動化が組み込まれていない(Certbot/legoまたはData Plane APIと組み合わせる)。
  • デフォルトでは手動でのサービス検出(動的バックエンドにはテンプレート/APIが必要)。
  • 組み込みのキャッシュ/静的サービングに制限がある(必要に応じてCDN/Varnish/Nginxを使用)。
  • ネイティブのコミュニティWAFがない(別のWAFかHAProxy Enterpriseを使う)。
  • 複雑な書き換えをすると、言葉が多くなることがある
  • Windowsのサポートは限定的(Linux/BSDがベスト)。

必要なもの

  • HAProxy(リバースプロキシ)用のVPS/パブリックサーバ。
  • オリジンサーバー(例:10.0.0.10:8080)。
  • HAProxyサーバーのパブリックIPを指すDNS A/AAAの ドメイン(例:example.com)。

プライバシーに関するヒント:オリジンIPを本当に隠すには、オリジンが一般に到達可能でないことを確認し、HAProxyサーバーからのトラフィックのみを受け入れるようにファイアウォールを設定し、オリジンを明らかにするDNSレコードを避ける。


ステップ1 – HAProxyのインストール

Ubuntu/Debian

sudo apt update sudo apt install -y haproxy

RHEL/Alma/Rocky

sudo dnf install -y haproxy

ステップ2 – TLS証明書を取得する(Let’s Encrypt)

Certbotに証明書を取得させ、HAProxy用にバンドルする。

certbotをインストールし、証明書を取得する。

# Ubuntu/Debian sudo apt install -y certbot sudo certbot certonly --standalone -d example.com --agree-tos -m you@example.com --non-interactive

HAProxyのPEMバンドル(fullchain + privkey)を作成する。

sudo mkdir -p /etc/haproxy/certs sudo bash -c 'cat /etc/letsencrypt/live/example.com/fullchain.pem ୧ /etc/letsencrypt/live/example.com/privkey.pem ୧ > /etc/haproxy/certs/example.com.pem' sudo chmod 600 /etc/haproxy/certs/example.com.pem

更新時にHAProxyを自動リバンドル&リロードする

sudo bash -c 'cat >/etc/letsencrypt/renewal-hooks/deploy/haproxy.sh' <<'EOF' #!/usr/bin/env bash cat /etc/letsencrypt/live/example.com/fullchain.pem ୧ /etc/letsencrypt/live/example.com/privkey.pem ୧ > /etc/haproxy/certs/example.com.pem systemctl reload haproxy EOF sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh

ステップ3 – 最小限の本番用HAProxy設定(HTTPS + リダイレクト)

example.comとあなたのバックエンドのIP/ポートを置き換える

# /etc/haproxy/haproxy.cfg global log /dev/log local0 maxconn 50000 daemon defaults log global mode http option httplog timeout connect 5s timeout client 60s timeout server 60s http-reuse safe # Frontend: listen on 80/443, redirect to HTTPS, route ACME and app traffic frontend fe_https bind :80 bind :443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1 # http-request redirect scheme https unless { ssl_fc } # 基本的なセキュリティヘッダ http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" if { ssl_fc } # アプリのクライアント情報を保持する option forwardfor header X-Forwarded-For http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Host %[req.hdr(host)] # 単純なレートキャップ:100リクエスト/10s/IP stick-table type ip size 100k expire 10m store http_req_rate(10s) http-request track-sc0 src acl too_fast sc0_http_req_rate gt 100 http-request deny status 429 if too_fast # ACME HTTP-01チャレンジをローカルのcertbotにルーティング (更新時に使用) acl acme path_beg /.well-known/acme-challenge/ use_backend be_acme if acme # ドメインをオリジンのバックエンドにルートする acl host_example hdr(host) -i example.com use_backend be_app if host_example default_backend be_app # バックエンド: あなたのオリジンサーバー backend be_app balance leastconn option httpchk GET /health http-check expect status 200 server app1 10.0.0.10:8080 check # ACMEチャレンジを提供するバックエンド(certbotスタンドアロンフック) backend be_acme server local 127.0.0.1:8081

なぜこれが動作するのか?

  • HAProxyは:443でTLSを終了し、:80をHTTPSにリダイレクトする。
  • 通常のトラフィックは10.0.0.10:8080のオリジンに送られます。
  • /.well-known/acme-challenge/*のみが、更新時に実行される小さなローカルWebサーバーCertbotにルーティングされます。

ステップ4 – 起動、リロード、検証

# 設定を検証する sudo haproxy -c -f /etc/haproxy/haproxy.cfg # 有効にして起動する sudo systemctl enable --now haproxy # 編集/更新後にリロードする sudo systemctl reload haproxy

ステップ5 – ハンズオフ更新

HAProxyが:80/:443をオープンにしている間、Certbotに:8081に一時的にバインドさせる:

# 通常はsystemdタイマーが処理する。sh" ¶ --http-01-port 8081 --pre-hook "systemctl start haproxy" --post-hook "systemctl start haproxy"

更新中、Certbotはポート8081でチャレンジに答える。HAProxyはすでにそのパスを127.0.0.1:8081にルーティングしている。


バリエーション(必要なものを選んでください)

A) ホスト名による複数のオリジン

# フロントエンドに追加: acl host_api hdr(host) -i api.example.com use_backend be_api if host_api # APIバックエンドを定義: backend be_api balance roundrobin option httpchk GET /healthz server api1 10.0.0.21:9000 check server api2 10.0.0.22:9000 check

B) TLSパススルー(オリジンがTLS/mTLSを処理する)

SNIルーティングでTCPモードを使用。

frontend fe_tcp mode tcp bind :443 tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend be_tls_app if { req_ssl_sni -i example.com } backend be_tls_app mode tcp server app_tls 10.0.0.10:443 check

C)最小限のHTTPのみのリバースプロキシ(TLSなし)

global log /dev/log local0 defaults mode http log global option httplog timeout connect 5s timeout client 60s timeout server 60s frontend public_http bind :80 option forwardfor default_backend app backend app server app1 10.0.0.10:8080 check

簡単なチェックとトラブルシューティング

# DNSがHAProxyを指していること dig +short example.com # HTTPがHTTPSにリダイレクトされること(301) curl -I http://example.com # HTTPSがコンテンツを提供すること curl -I https://example.com # アプリが受け取るヘッダを見る(アプリのログで): # X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host

ファイアウォールのヒント:

  • X-Forward-For、X-Forwarded-Proto、X-Forwarded-Host ファイアウォールのヒント:オリジンをロックして、HAProxyサーバーからのトラフィックのみを受け付けるようにする(例:ufwfirewalld、またはクラウドセキュリティグループを使用)。
  • オプションとして、プロバイダレベルでオリジンIPへの直接パブリックアクセスをブロックする。

最終的な注意事項

  • タイムアウトは、ワークロードに対して妥当な値に保つこと(WebSocket/gRPCはより高い値が必要な場合がある)。
  • httpchk用のアプリで/healthエンドポイントを公開する。
  • ゼロダウンタイムのデプロイを計画する: デプロイ中にサーバーから水を抜き(無効化)、その後再有効化する。

重要なお知らせ:サーバーを正しく設定する方法が不明な場合は、専門家に設定を依頼することを強くお勧めします。ファイアウォールのポートを確認し、ポートブロックがないことを確認するなど、すべての設定が正確に行われていることを確認することが重要です。 設定プロセスを効果的に進めるためには、少なくともファイアウォールとLinuxコマンドの基本的な理解を持っていることが重要です。 設定プロセスで発生する可能性のある損害や問題については、弊社では責任を負いかねますのでご了承ください。ここで提供されるすべての情報は、技術的な知識と学習のみを目的としています。 ご理解のほど、よろしくお願いいたします。