ちょっと話題の記事

Network Load Balancer (NLB) がセキュリティグループをサポートして何が嬉しいのか整理してみた

今後作成するNLBにはセキュリティグループは絶対割り当てたい
2023.08.11

うおおおおおおおおお!!! ってなったけどなんで歓喜しているんだっけ?

こんにちは、のんピ(@non____97)です。

皆さんはNetwork Load Balancer (以降NLB) にセキュリティグループをアタッチしたいなと思ったことはありますか? 私はあります。

本日8/11についにサポートされましたね! 早速、偉大な先人がアップデートをまとめてくれています。

細かい仕様は以下AWS公式ドキュメントにも記載があります。

うおおおおおおおおおおおおお!!!

という感じで目が覚めたのですが、なぜ私はこんなに歓喜しているんでしょうか。

この感情を言語化してみました。

いきなりまとめ

  • クライアントIPアドレスを保持する場合、NLBを介さずに直接アクセスされるのを防ぐことが簡単になった
    • 従来はターゲットのセキュリティグループでクライアントのIPアドレスからの許可をする必要があったため、NLBを介さずにアクセスすることが容易
  • クライアントIPアドレスを保持しない場合でも、アクセス制限できるようになった
    • 以前は無理矢理実装しようとする場合、PrivateLinkやNACLを組み合わせる必要があった
  • ターゲットのセキュリティグループのインバウンドルールがシンプルになる
    • NLBのセキュリティグループのIDを許可すればOK
  • 送信元のセキュリティグループのIDでも通信が制御できるようになった
  • ヘルスチェックもNLBのIPアドレスではなく、NLBのセキュリティグループのIDで制御できるようになった
  • PrivateLink経由でアクセスしてくる場合も、PrivateLink トラフィックにインバウンドルールを適用するを有効化することで、アクセス元のIPアドレスで制御できるようになった
  • オープンなサービスエンドポイントであるならば、PrivateLink トラフィックにインバウンドルールを適用するは無効化しよう
  • 今後作成するNLBにはセキュリティグループは絶対割り当てたい

クライアントIPアドレスを保持する場合、NLBを介さずに直接アクセスされるのを防ぐことが簡単になった

なんと言ってもこれでしょう。

以下記事でまとめた通り、クライアントIPアドレスの保持を有効にすると、ターゲットが受け取る送信元IPアドレスはクライアントのIPアドレスとなります。

つまりは、ターゲットのセキュリティグループではクライアントのIPアドレスを許可する必要が出てきます。

特に、Internet facingのNLBの場合にクライアントIPアドレスを保持すると、ターゲットのセキュリティグループでは0.0.0.0/0を許可する必要が出てきます。

となると困るのが、ターゲットのセキュリティグループでクライアントのIPアドレスを許可している関係上、NLBを介さなくともアクセスできてしまいます。

NLBを介さなくともアクセス可能

抜粋 : クライアントIPアドレスの保持

また、NLBのアクセスログをS3バケットに出力してくれるのは、TLSリクエストのみです。

アクセスログが作成されるのは、ロードバランサーに TLS リスナーがあり、TLS リクエストに関する情報のみを含む場合のみです。

Network Load Balancer のアクセスログ - Elastic Load Balancing

クライアントIPアドレスの保持を無効化した場合、TLSリクエスト以外はどのクライアントからのアクセスなのか判断することができません。

クライアントIPアドレスの保持を無効化した場合のターゲットに記録されるIPアドレス

ターゲットとしても、クライアントIPアドレスで処理を振り分ける場合は機能しなくなります。

例えば、PostfixのmynetworksをNLBのIPアドレスとしても、送信元IPアドレスは全てNLBのIPアドレスとなってしまうため、NLBに到達できるすべてのメールクライアントからのメールをリレーしてしまいます。

NLBに到達可能なメールクライアントからのメールを全てリレーしてしまう

そのため、個人的にはクライアントIPアドレスの保持は有効化しておきたいところです。

今回のアップデートで、「クライアントIPアドレスは保持したい。でも、NLBを経由せずに直接アクセスされるのは困る」というお悩みを解消することができます。

クライアントIPアドレスを保持する場合、NLBを介さずに直接アクセスされるのを防ぐことが簡単になった

クライアントIPアドレスを保持しない場合でも、アクセス制限できるようになった

クライアントIPアドレスの保持を無効にした場合は、ターゲットのセキュリティグループでNLBのIPアドレスのみに通信を制御したとしても、効果は薄いというお話を先ほどしました。

以前は、無理矢理NLBの送信元IPアドレスを制御しようとする場合は、PrivateLinkやNACLを組み合わせる必要がありました。

無理矢理実装しようとする場合はPrivateLinkやNACLを組み合わせる必要があった

抜粋 : Network Load Balancer (NLB) のソースIPアドレスに思いを馳せてみた | DevelopersIO

アップデートにより、NLBでセキュリティグループを使ったアクセス制御ができるため、上述の非常に面倒な実装を行う必要がなくなりました。

クライアントIPアドレスを保持しない場合でも、アクセス制限できるようになった

ターゲットのセキュリティグループのインバウンドルールがシンプルになる

ターゲットとなるEC2インスタンスでは、NLBのセキュリティグループのIDからの通信を許可するようなインバウンドルールを定義するだけになるため、非常にシンプルです。

クライアントIPアドレスの保持を有効/無効を考慮して、インバウンドルールに頭を悩ませる必要は無くなりました。

アクセス制御をNLBのセキュリティグループに委任することができます。

送信元のセキュリティグループのIDでもアクセス制御ができるようになった

ターゲットグループの仕様として、ターゲットのセキュリティグループで、クライアントのセキュリティグループIDを使ったアクセス制御はできません。

ターゲットセキュリティグループ

EC2 インスタンスをターゲットとして登録する場合は、これらのインスタンスのセキュリティグループがリスナーポートとヘルスチェックポートの両方でトラフィックを許可していることを確認する必要があります。

考慮事項

  • Network Load Balancer には関連付けられたセキュリティグループがありません。したがって、ターゲットのセキュリティグループは、トラフィックを許可するために IP アドレスを使用する必要があります。
  • クライアントのセキュリティグループを、ターゲットのセキュリティグループのソースとして使用することはできません。したがって、ターゲットのセキュリティグループは、トラフィックを許可するためにクライアントの IP アドレスを使用する必要があります。

ターゲットグループへのターゲットの登録 - Elastic Load Balancing

(2023/8/11時点の日本語版のドキュメントでは、まだNLBがセキュリティグループをサポートしたアップデートが反映されていませんでした)

そのため、以下のようなアクセス制御はできません。必ずIPアドレスで許可する必要がありました。

クライアントのセキュリティグループを、ターゲットのセキュリティグループのソースとして使用することはできません

アクセスを許可したいクライアントの数が増減する場合、逐次ターゲットのセキュリティグループのインバウンドルールを変更する必要があり、非常に大変です。

今回のアップデートにより、NLBのセキュリティグループにて送信元のセキュリティグループIDからの通信を許可するようなルールを定義することで、このような煩わしさから解放されます。

送信元のセキュリティグループのIDでもアクセス制御ができるようになった

ヘルスチェックもNLBのIPアドレスではなく、NLBのセキュリティグループのIDで制御できるようになった

以前はヘルスチェック用にNLBのIPアドレスを調べて許可する必要がありました。(AWS公式ドキュメントにはVPC CIDRで許可するとなっていますが、NLBのIPアドレスで十分です)

クライアント IP の保存が有効な場合の推奨ルール

クライアント IP の保存が有効になっているターゲットのセキュリティグループに対して推奨されるルールを以下に示します。

インバウンドルール

ソース プロトコル ポート範囲 コメント
クライアント CIDR target target Allow traffic from your application, your network, or the internet
VPC CIDR ヘルスチェック ヘルスチェック Allow health check traffic from the load balancer

ターゲットグループへのターゲットの登録 - Elastic Load Balancing

今回のアップデートにより、NLBのセキュリティグループのIDからの通信を許可するようなインバウンドルールを定義するだけになるため、非常にシンプルです。

Recommendations for target security groups if the load balancer has an associated security group

  • To allow client traffic: Add a rule that references the security group associated with the load balancer.
  • To allow PrivateLink traffic: If you configured the load balancer to evaluate inbound rules for traffic sent through AWS PrivateLink, add a rule that accepts traffic from the load balancer security group on the traffic port. Otherwise, add a rule that accepts traffic from the load balancer private IP addresses on the traffic port.
  • To accept load balancer health checks: Add a rule that accepts health check traffic from the load balancer security groups on the health check port.

Register targets with your target group - Elastic Load Balancing

動作確認

検証環境

実際に動作確認してみましょう。検証環境は以下のとおりです。

Network Load Balancer (NLB) がセキュリティグループをサポートして何が嬉しいのか整理してみた検証環境構成図

セキュリティグループのインバウンドルールは以下のようにしています。

  • NLB : ClientからのTCP/80のみ許可
  • Nginx : NLBからのTCP/80のみ許可

ターゲットグループの作成からやってみます。

まず、ターゲットグループの作成をします。

> aws elbv2 create-target-group \
    --name nginx \
    --protocol TCP \
    --port 80 \
    --vpc-id vpc-003be9f32aaa12cef
{
    "TargetGroups": [
        {
            "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7",
            "TargetGroupName": "nginx",
            "Protocol": "TCP",
            "Port": 80,
            "VpcId": "vpc-003be9f32aaa12cef",
            "HealthCheckProtocol": "TCP",
            "HealthCheckPort": "traffic-port",
            "HealthCheckEnabled": true,
            "HealthCheckIntervalSeconds": 30,
            "HealthCheckTimeoutSeconds": 10,
            "HealthyThresholdCount": 5,
            "UnhealthyThresholdCount": 2,
            "TargetType": "instance",
            "IpAddressType": "ipv4"
        }
    ]
}

ターゲットグループにNginxをインストールしたEC2インスタンスを登録します。

> aws elbv2 register-targets \
    --target-group-arn arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7 \
    --targets Id=i-04596ec4ddd13705c

ターゲットグループの属性を確認して、クライアントIPアドレスの保持preserve_client_ip.enabledが有効になっていることを確認します。

> aws elbv2 describe-target-group-attributes \
    --target-group-arn arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7
{
    "Attributes": [
        {
            "Key": "proxy_protocol_v2.enabled",
            "Value": "false"
        },
        {
            "Key": "target_group_health.unhealthy_state_routing.minimum_healthy_targets.count",
            "Value": "1"
        },
        {
            "Key": "preserve_client_ip.enabled",
            "Value": "true"
        },
        {
            "Key": "stickiness.enabled",
            "Value": "false"
        },
        {
            "Key": "target_group_health.unhealthy_state_routing.minimum_healthy_targets.percentage",
            "Value": "off"
        },
        {
            "Key": "deregistration_delay.timeout_seconds",
            "Value": "300"
        },
        {
            "Key": "target_group_health.dns_failover.minimum_healthy_targets.count",
            "Value": "1"
        },
        {
            "Key": "stickiness.type",
            "Value": "source_ip"
        },
        {
            "Key": "deregistration_delay.connection_termination.enabled",
            "Value": "false"
        },
        {
            "Key": "load_balancing.cross_zone.enabled",
            "Value": "use_load_balancer_configuration"
        },
        {
            "Key": "target_group_health.dns_failover.minimum_healthy_targets.percentage",
            "Value": "off"
        }
    ]
}

NLBを作成します。作成時に事前に作成しておいたセキュリティグループを指定しています。

> aws elbv2 create-load-balancer \
    --name nlb-with-sg \
    --type network \
    --subnets subnet-060ed03483293aaab \
    --scheme internal \
    --security-groups sg-09dd2d6508aec1de9
{
    "LoadBalancers": [
        {
            "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:loadbalancer/net/nlb-with-sg/a6e92d2aad75a60e",
            "DNSName": "nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com",
            "CanonicalHostedZoneId": "Z26RNL4JYFTOTI",
            "CreatedTime": "2023-08-11T02:31:24.226000+00:00",
            "LoadBalancerName": "nlb-with-sg",
            "Scheme": "internal",
            "VpcId": "vpc-003be9f32aaa12cef",
            "State": {
                "Code": "provisioning"
            },
            "Type": "network",
            "AvailabilityZones": [
                {
                    "ZoneName": "us-east-1d",
                    "SubnetId": "subnet-060ed03483293aaab",
                    "LoadBalancerAddresses": []
                }
            ],
            "SecurityGroups": [
                "sg-09dd2d6508aec1de9"
            ],
            "IpAddressType": "ipv4"
        }
    ]
}

NLBのリスナーを作成します。先ほど作成したターゲットグループに転送するように指定してあげます。

> aws elbv2 create-listener \
    --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:loadbalancer/net/nlb-with-sg/a6e92d2aad75a60e \
    --protocol TCP \
    --port 80 \
    --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7
{
    "Listeners": [
        {
            "ListenerArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:listener/net/nlb-with-sg/a6e92d2aad75a60e/5df3f20340f70bb6",
            "LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:loadbalancer/net/nlb-with-sg/a6e92d2aad75a60e",
            "Port": 80,
            "Protocol": "TCP",
            "DefaultActions": [
                {
                    "Type": "forward",
                    "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7",
                    "ForwardConfig": {
                        "TargetGroups": [
                            {
                                "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7"
                            }
                        ]
                    }
                }
            ]
        }
    ]
}

ターゲットがhealthyになっているか確認しておきます。

> aws elbv2 describe-target-health \
    --target-group-arn arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7
{
    "TargetHealthDescriptions": [
        {
            "Target": {
                "Id": "i-04596ec4ddd13705c",
                "Port": 80
            },
            "HealthCheckPort": "80",
            "TargetHealth": {
                "State": "healthy"
            }
        }
    ]
}

クライアントIPアドレスの保持を有効化している場合のアクセス元IPアドレス

それでは、クライアントのEC2インスタンスからNLBに対してアクセスしてみましょう。

# クライアントのEC2インスタンスのIPアドレスの確認
$ hostname -i
10.0.0.48

# NLBに対してアクセス
$ curl -I http://nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Fri, 11 Aug 2023 02:52:00 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 31 May 2023 21:09:22 GMT
Connection: keep-alive
ETag: "6477b782-267"
Accept-Ranges: bytes

$ curl -v http://nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com
*   Trying 10.0.1.230:80...
* Connected to nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com (10.0.1.230) port 80 (#0)
> GET / HTTP/1.1
> Host: nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0
< Date: Fri, 11 Aug 2023 02:52:04 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Wed, 31 May 2023 21:09:22 GMT
< Connection: keep-alive
< ETag: "6477b782-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com left intact

問題なくアクセスできましたね。

Nginx側でアクセスログを確認します。

# NginxのEC2インスタンスのIPアドレス確認
$ hostname -i
10.0.1.47

# Nginxのアクセスログ
$ sudo tail /var/log/nginx/access.log
10.0.0.48 - - [11/Aug/2023:02:52:00 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.0.1" "-"
10.0.0.48 - - [11/Aug/2023:02:52:04 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "-"

クライアントのIPアドレスがログに記録されていますね。

なお、NLBを介さずにアクセスした場合は以下のようにタイムアウトとなりました。しっかりとNLBのセキュリティグループからの通信のみ許可していることが分かります。

$ curl -v -m 5 http://10.0.1.47
*   Trying 10.0.1.47:80...
* Connection timed out after 5001 milliseconds
* Closing connection 0
curl: (28) Connection timed out after 5001 milliseconds

クライアントIPアドレスの保持を無効化している場合のアクセス元IPアドレス

クライアントIPアドレスの保持を無効化している場合のアクセス元IPアドレスも確認します。

クライアントIPアドレスの保持を無効化します。

> aws elbv2 modify-target-group-attributes \
    --target-group-arn arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:targetgroup/nginx/9eb2dbdd383669f7 \
    --attributes Key=preserve_client_ip.enabled,Value=false
{
    "Attributes": [
        {
            "Key": "proxy_protocol_v2.enabled",
            "Value": "false"
        },
        {
            "Key": "target_group_health.unhealthy_state_routing.minimum_healthy_targets.count",
            "Value": "1"
        },
        {
            "Key": "preserve_client_ip.enabled",
            "Value": "false"
        },
        {
            "Key": "stickiness.enabled",
            "Value": "false"
        },
        {
            "Key": "target_group_health.unhealthy_state_routing.minimum_healthy_targets.percentage",
            "Value": "off"
        },
        {
            "Key": "deregistration_delay.timeout_seconds",
            "Value": "300"
        },
        {
            "Key": "target_group_health.dns_failover.minimum_healthy_targets.count",
            "Value": "1"
        },
        {
            "Key": "stickiness.type",
            "Value": "source_ip"
        },
        {
            "Key": "deregistration_delay.connection_termination.enabled",
            "Value": "false"
        },
        {
            "Key": "load_balancing.cross_zone.enabled",
            "Value": "use_load_balancer_configuration"
        },
        {
            "Key": "target_group_health.dns_failover.minimum_healthy_targets.percentage",
            "Value": "off"
        }
    ]
}

この状態でアクセスしてみます。

$ curl -I http://nlb-with-sg-a6e92d2aad75a60e.elb.us-east-1.amazonaws.com
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Fri, 11 Aug 2023 03:03:47 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 31 May 2023 21:09:22 GMT
Connection: keep-alive
ETag: "6477b782-267"
Accept-Ranges: bytes

HTTP 200になりました。

Nginxのアクセスログを確認してみましょう。

$ sudo tail /var/log/nginx/access.log
.
.
(中略)
.
.
10.0.1.230 - - [11/Aug/2023:03:03:47 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.0.1" "-"

今度はNLBのIPアドレスが記録されましたね。

PrivateLink経由でアクセスした場合ののアクセス元IPアドレス

PrivateLink経由でアクセスした場合ののアクセス元IPアドレスも確認します。

構成図は以下のとおりです。

Network Load Balancer (NLB) がセキュリティグループをサポートして何が嬉しいのか整理してみた検証環境構成図 (PrivateLink)

PrivateLinkのVPC Endpointのセキュリティグループでは、ClientからのTCP/80のみ許可するようにしてあげます。その他のセキュリティグループは変更しません。

また、クライアントIPアドレスの保持は有効化しておきます。

まず、エンドポイントサービスを作成します。

> aws ec2 create-vpc-endpoint-service-configuration \
    --network-load-balancer-arns arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:loadbalancer/net/nlb-with-sg/a6e92d2aad75a60e \
    --no-acceptance-required
{
    "ServiceConfiguration": {
        "ServiceType": [
            {
                "ServiceType": "Interface"
            }
        ],
        "ServiceId": "vpce-svc-00e9c39232c84d079",
        "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-00e9c39232c84d079",
        "ServiceState": "Available",
        "AvailabilityZones": [
            "us-east-1d"
        ],
        "AcceptanceRequired": false,
        "ManagesVpcEndpoints": false,
        "NetworkLoadBalancerArns": [
            "arn:aws:elasticloadbalancing:us-east-1:<AWSアカウントID>:loadbalancer/net/nlb-with-sg/a6e92d2aad75a60e"
        ],
        "SupportedIpAddressTypes": [
            "ipv4"
        ],
        "BaseEndpointDnsNames": [
            "vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com"
        ],
        "PrivateDnsNameConfiguration": {}
    }
}

次にVPC A上にVPC Endpointを作成します。

> aws ec2 create-vpc-endpoint \
    --vpc-endpoint-type Interface \
    --vpc-id vpc-0225978c95743ab75 \
    --subnet-ids subnet-0719f6727685295be \
    --security-group-ids sg-09252835599e49e7a
    --service-name com.amazonaws.vpce.us-east-1.vpce-svc-00e9c39232c84d079 \
{
    "VpcEndpoint": {
        "VpcEndpointId": "vpce-086b08494e8a77480",
        "VpcEndpointType": "Interface",
        "VpcId": "vpc-0225978c95743ab75",
        "ServiceName": "com.amazonaws.vpce.us-east-1.vpce-svc-00e9c39232c84d079",
        "State": "pending",
        "RouteTableIds": [],
        "SubnetIds": [
            "subnet-0719f6727685295be"
        ],
        "Groups": [
            {
                "GroupId": "sg-09252835599e49e7a",
                "GroupName": "SG-PRIVATELINK"
            }
        ],
        "IpAddressType": "ipv4",
        "DnsOptions": {
            "DnsRecordIpType": "ipv4"
        },
        "PrivateDnsEnabled": false,
        "RequesterManaged": false,
        "NetworkInterfaceIds": [
            "eni-00e1b85e7680e50e1"
        ],
        "DnsEntries": [
            {
                "DnsName": "vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com",
                "HostedZoneId": "Z7HUB22UULQXV"
            },
            {
                "DnsName": "vpce-086b08494e8a77480-omwyqmcn-us-east-1d.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com",
                "HostedZoneId": "Z7HUB22UULQXV"
            }
        ],
        "CreationTimestamp": "2023-08-11T03:18:40.661000+00:00",
        "OwnerId": "<AWSアカウントID>"
    }
}

作成後、VPC EndpointのDNS名に対してアクセスしてみます。

$ curl -v -m 5 vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com
*   Trying 10.1.1.14:80...
* Connection timed out after 5000 milliseconds
* Closing connection 0
curl: (28) Connection timed out after 5000 milliseconds

タイムアウトになってしまいました。

デフォルトではPrivateLink トラフィックにインバウンドルールを適用するは有効化されています。

PrivateLink トラフィックにインバウンドルールを適用する

試しに無効化してみましょう。

PrivateLink トラフィックにインバウンドルールを適用するを無効

設定変更後、再度アクセスします。

$ curl -v -m 5 vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com
*   Trying 10.1.1.14:80...
* Connected to vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com (10.1.1.14) port 80 (#0)
> GET / HTTP/1.1
> Host: vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com
> User-Agent: curl/8.0.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0
< Date: Fri, 11 Aug 2023 03:23:07 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Wed, 31 May 2023 21:09:22 GMT
< Connection: keep-alive
< ETag: "6477b782-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host vpce-086b08494e8a77480-omwyqmcn.vpce-svc-00e9c39232c84d079.us-east-1.vpce.amazonaws.com left intact

アクセスできました。

Nginxのアクセスログも確認します。

$ sudo tail /var/log/nginx/access.log
.
.
(中略)
.
.
10.0.1.230 - - [11/Aug/2023:03:23:07 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.0.1" "-"

NLBのIPアドレスが記録されましたね。

PrivateLink トラフィックにインバウンドルールを適用するを有効化した状態でも何とかアクセスできないか試してみます。

結果は以下のとおりです。

  • NLBのセキュリティグループで、PrivateLinkのセキュリティグループIDからの通信を許可する : アクセスできない
  • NLBのセキュリティグループで、NLBのセキュリティグループIDからの通信を許可する : アクセスできない
  • NLBのセキュリティグループで、ClientのセキュリティグループIDからの通信を許可する : アクセスできない
  • NLBのセキュリティグループで、VPC AのCIDR10.0.0.0/24からの通信を許可する : アクセスできる (アクセス元IPアドレスはNLBのIPアドレス)
  • NLBのセキュリティグループで、ClientのIPアドレス10.0.0.48/32からの通信を許可する : アクセスできる (アクセス元IPアドレスはNLBのIPアドレス)

PrivateLink トラフィックにインバウンドルールを適用するを有効化した場合、PrivateLink経由での通信はセキュリティグループIDでの制御はできませんでしたが、クライアントのIPアドレスによる制御は可能なようです。

本当はPrivateLink経由の通信がAnyで許可したくないという場合は、PrivateLink トラフィックにインバウンドルールを適用するを有効化すると良いでしょう。

一方、オープンなサービスエンドポイントであるならば、PrivateLink トラフィックにインバウンドルールを適用するは無効化しましょう。そのような場合はクライアントのIPアドレスは不特定多数だと考えます。NLBのセキュリティグループで0.0.0.0/0を許可してしまうとPrivateLinkを介さない通信も全通しとなってしまいます。PrivateLink トラフィックにインバウンドルールを適用するを無効化することで、PrivateLink経由の通信のみAnyでアクセスが許可されます。

今後作成するNLBには絶対にセキュリティグループを割り当てよう

NLBがセキュリティグループをサポートして何が嬉しいのか整理してみました。

セキュリティグループを割り当てて困るようなシチェーションは見当たりませんでした。今後作成するNLBには絶対にセキュリティグループを割り当てましょう。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!