この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
清水です。AWSのHTTP/HTTPS向けLoad BalancingサービスであるALB (Application Load Balancer)ではリソース(Load Balancer)ごとにDNS名が割り振られます。使用の際にはこのDNS名を独自ドメインに割り当てて使用することが多いかと思いますが、特に設定をしない場合だと独自ドメイン以外でもDNS名や、それを名前解決したIPアドレスでALBにアクセスが可能です。独自ドメイン、つまりアクセスする際のHostヘッダの内容によってこのアクセスを制限する方法については、例えばAWS WAFを使ったり、バックエンドのEC2側でHostヘッダをチェックしたりといくつか方法が考えられます。今回は今回はALB機能のみを使用して実現することを考えてみました。具体的には、ALBのHostベースルーティング機能と固定レスポンス機能を組み合わせ、意図したHostヘッダの場合のみ背後のEC2にルーティング、それ以外は固定レスポンスを返す方法で実現をしています。
ALBはIPアドレス直でもアクセス可能
まずはALBデフォルトの動作を確認しておきます。ALBはHTTP:80とHTTPS:443でリスンするように設定、HTTPS:443には「www.example.net」用のACM証明書を設定してあります。またRoute 53で「www.example.net」に対してA ALIASレコードで、このALBのDNS名「test-alb-1234567890.ap-northeast-1.elb.amazonaws.com」を設定してある状態です。リスナールールは特に指定していないため、すべてのリクエストが背後のEC2にルーティングされる状態です。背後のEC2ではApache+PHPを動作させています。
独自ドメイン名へのアクセスは当然、意図通り行えます。
$ curl -i http://www.example.net/
HTTP/1.1 200 OK
Date: Sun, 31 Oct 2021 09:05:53 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 16
Connection: keep-alive
Server: Apache/2.4.51 () PHP/5.4.16
Upgrade: h2,h2c
Last-Modified: Sun, 31 Oct 2021 08:56:43 GMT
ETag: "10-5cfa23d50774f"
Accept-Ranges: bytes
www.example.net
$ curl -i https://www.example.net/
HTTP/2 200
date: Sun, 31 Oct 2021 09:05:57 GMT
content-type: text/html; charset=UTF-8
content-length: 16
server: Apache/2.4.51 () PHP/5.4.16
last-modified: Sun, 31 Oct 2021 08:56:43 GMT
etag: "10-5cfa23d50774f"
accept-ranges: bytes
www.example.net
ALBへのDNS名へのアクセスも可能です。HTTPSについては証明書関連のエラーが表れますが、これを-k
オプションなどで無視すればアクセスは可能となります。
$ curl -i http://test-alb-1234567890.ap-northeast-1.elb.amazonaws.com/
HTTP/1.1 200 OK
Date: Sun, 31 Oct 2021 09:06:54 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 16
Connection: keep-alive
Server: Apache/2.4.51 () PHP/5.4.16
Upgrade: h2,h2c
Last-Modified: Sun, 31 Oct 2021 08:56:43 GMT
ETag: "10-5cfa23d50774f"
Accept-Ranges: bytes
www.example.net
$ curl -i https://test-alb-1234567890.ap-northeast-
1.elb.amazonaws.com/
curl: (60) SSL: no alternative certificate subject name matches target host name 'test-alb-1234567890.ap-northeast-1.elb.amazonaws.com'
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
$ curl -i -k https://test-alb-1234567890.ap-northea
st-1.elb.amazonaws.com/
HTTP/2 200
date: Sun, 31 Oct 2021 09:07:43 GMT
content-type: text/html; charset=UTF-8
content-length: 16
server: Apache/2.4.51 () PHP/5.4.16
last-modified: Sun, 31 Oct 2021 08:56:43 GMT
etag: "10-5cfa23d50774f"
accept-ranges: bytes
www.example.net
IPアドレス直指定の場合も確認してみます。こちらもDNS名のときと同様、HTTPSの場合は証明書関連のエラーが出ますが、これを無視してしまえばアクセス自体は可能です。
$ dig test-alb-1234567890.ap-northeast-1.elb.amazonaws.com +short
52.XXX.XX.180
52.XXX.XXX.161
$ curl -i http://52.XXX.XX.180/
HTTP/1.1 200 OK
Date: Sun, 31 Oct 2021 09:09:22 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 16
Connection: keep-alive
Server: Apache/2.4.51 () PHP/5.4.16
Upgrade: h2,h2c
Last-Modified: Sun, 31 Oct 2021 08:56:43 GMT
ETag: "10-5cfa23d50774f"
Accept-Ranges: bytes
www.example.net
$ curl -i https://52.XXX.XX.180/
curl: (60) SSL: no alternative certificate subject name matches target host name '52.XXX.XX.180'
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
$ curl -i -k https://52.XXX.XX.180/
HTTP/2 200
date: Sun, 31 Oct 2021 09:09:29 GMT
content-type: text/html; charset=UTF-8
content-length: 16
server: Apache/2.4.51 () PHP/5.4.16
last-modified: Sun, 31 Oct 2021 08:56:43 GMT
etag: "10-5cfa23d50774f"
accept-ranges: bytes
www.example.net
ALBへのIPアドレス直指定のアクセスをどう捉えるか
ALBへのIPアドレス直接指定でのアクセスについて考えてみます。ALBのIPアドレスは可変であり、そのうち変わる可能性が高いです。しかし変わるということは、昔どこかの誰かが使用していたIPアドレスがALBにたまたま関連付く可能性もあるわけです。このことからも、意図せずIPアドレス直指定でアクセスされる可能性も否めません。
一般的なユーザからIPアドレス直指定で単純に間違ったアクセスがある、という場合はおそらくHTTPSではなくてHTTPになるかと思います。HTTPSでは証明書のエラーが出るため、そこでアクセスを止めてしまいますよね。このような、たまたHTTPでIPアドレス直指定でのアクセスがあった場合、丁寧に正しいURLをHTTPSでお知らせするのも1つの方法かと思います。以下のように、HTTPのリスナールールでHTTPSへのリダイレクトを入れる際、Hostヘッダを独自ドメイン名で指定しておけば、これが実現できますね。
$ curl -i http://52.XXX.XX.180/
HTTP/1.1 301 Moved Permanently
Server: awselb/2.0
Date: Sun, 31 Oct 2021 09:18:30 GMT
Content-Type: text/html
Content-Length: 134
Connection: keep-alive
Location: https://www.example.net:443/
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
</body>
</html>
ただし、間違ったIPアドレスでやってきたユーザや、もしくはIPアドレス総当りでなにか攻撃をしているような輩にわざわざHost名を教えるのも微妙だな、というケースもあるかと思います。このような場合には以下で示すように、Hostヘッダを具体的にチェックしてアクセスを制限する方法が採れるかと思います。
ALBのリスナールールでHostヘッダをチェックする
本題の、ALBへのアクセスをIPアドレス直指定や意図していないドメイン(Hostヘッダ)の場合に禁止する方法です。ALBの機能のみで実現する方法として、リスナールールでHostヘッダをチェックする方法が考えられます。ルールでは以下のように、想定のホスト名の場合はターゲットグループに転送、それ以外の場合はALB側で固定レスポンスを返します。Hostベースルーティング機能と固定レスポンス機能の組み合わせですね。固定レスポンスについて今回は403 Forbidden
を返すこととしました。想定のHostヘッダ以外のアクセスは、リソースにアクセスすることを拒否するという意図です。
HTTPについても同様の設定をしておきます。
Hostヘッダをチェックするよう設定をしてALBにアクセスした場合、DNS名やIPアドレスなど、想定していない(ALB側に設定していない)アドレス(Hostヘッダ)でアクセスすれば、ステータスコード403が返ります。
$ curl -i http://test-alb-1234567890.ap-northeast-1.elb.amazonaws.com/
HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Sun, 31 Oct 2021 09:38:49 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9
Connection: keep-alive
Forbidden
$ curl -i -k https://test-alb-1234567890.ap-northeast-1.elb.amazonaws.com/
HTTP/2 403
server: awselb/2.0
date: Sun, 31 Oct 2021 12:21:27 GMT
content-type: text/html; charset=utf-8
content-length: 9
Forbidden
$ curl -i http://52.XXX.XX.180/
HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Sun, 31 Oct 2021 09:38:59 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9
Connection: keep-alive
Forbidden
$ curl -i -k https://52.XXX.XX.180/
HTTP/2 403
server: awselb/2.0
date: Sun, 31 Oct 2021 12:22:18 GMT
content-type: text/html; charset=utf-8
content-length: 9
Forbidden
この方法の利点としてはALB側で設定できること、ALBのみで実現できることかなと思います。例えばEC2側でHostヘッダをチェックする、ということも検討できるかと思いますが、そのチェックのためにEC2のリソースを使用してしまうのもなにか釈然としないかな、と。できればEC2のリソースは使わず、その前段のALB側で処理ができれば、と思ったしだいです。
またAWS WAFにおいて、他のチェック項目と一緒にこのようなHostヘッダのチェックする、ということも検討できるかと思います。もちろんAWS WAFでより統合的にチェックを行うことのほうがメリットはあるかと思いますが、コストなどに制限がありAWS WAFまで使わなくても、という場合に、ALBの機能を用いてサクッと設定できるのではないかと思いました。
まとめ
ALBのリスナールールでHostヘッダのチェックを行い、想定したHostヘッダのアクセス(想定したドメインでのアクセス)以外は背後のEC2へのアクセスを行わせず、ALBの固定レスポンスを返す設定を行ってみました。機能的にはALBのHostベースルーティングと固定レスポンスを組み合わせたものですが、改めてALBでいろいろなことができるようになっていると思ったしだいです。