停用你的 EC2 IPv4 省錢!一併把服務轉到 IPv6 上



前言

  • 之前的 server 為了簡便,都是直接用 Elastic IP 直接對外連線。
  • 不過 AWS 自從 2024 年 2 月 1 日起,AWS 對於所有的 Elastic IP、Public IP 會開始收費 [1],大概每個月一個 IP 收 3.6 美金,大約是 100 元出頭 (貴)。
  • 晚了幾個月才知道的我,趕緊把幾台小 EC2 取消 Elastic IP (以及 Public IP),然後設法把流量轉到 IPv6 上。
  • 除了對外的流量,管理用的 SSH 連線方式也有更動,因為現在不能對外,所以要透過 AWS 的新功能 EC2 Instance Connect Endpoint 來連線;
  • SSH 連線方式官網示意圖:


大致的流程會是

  1. 首先,新增 IPv6 設定,然後把 EC2 的 public IPv4 移除 (private IP 還是可以使用 IPv4)。
  2. 然後新增 EC2 Instance Connect Endpoint (eice),並且新增 user 與 policy;這是用來以 SSH 登入連線管理使用。
  3. 最後更新 DNS,對外以 IPv6 提供服務



新增 IPv6,以及移除 EC2 的 public IPv4

新增 IPv6 的步驟

主要參考這篇 [2],還有動圖,非常清楚。

這邊列出主要的動作:

  1. 把預設的 (EC2 使用的) VPC 新增 IPv6 CIDR block:

    VPC -> [Actions] Edit CIDRs -> Add IPv6 CIDR

  2. 從你的 EC2 找到對應的 Subnet ID:

    Instance-> Networking -> Subnet ID

  3. 從你的 EC2 找到對應的 Network Interface ID:

    Instance-> Networking -> Network Interface ID -> [Actions] Manage IP addresses -> 點開 eth0 -> Assign new IP address (IPv6)

  4. 更新 Security Group,Inbound 允許 IPv6 port 22

    Security Group -> Inbound rules -> Edit inbound rules -> 新增 SSH & Source: ::/0

  5. 更新 Route Table,新增 IPv6 traffic 用同一個 internet gateway:

    Route Table -> Edit routes -> Add route -> Destination: ::/0 -> Internet Gateway: igw-xxxxxx


移除 EC2 public IPv4 的步驟

  1. 新增一個 Elastic IP

    Elastic IPs -> Allocate new address

  2. 然後把它綁定到 EC2 上

    Elastic IPs -> [Actions] Associate Elastic IP address -> 選擇對應的 EC2

  3. 新增一個新的 Network Interface

    Network Interfaces -> Create network interface -> 選擇對應的 Subnet ID、Security Group

  4. 等新建立的 Network Interface 的狀態準備就緒之後,把新的 Network Interface 綁定到 EC2 上

    Network Interfaces -> [Action] Attach network interface -> 選擇對應的 EC2

  5. 最後,把剛才綁定的 Elastic IP 移除

    Elastic IPs -> [Actions] Disassociate Elastic IP address -> [Actions] Release Elastic IP address

最後,回到 EC2 的 Instance 畫面,應該可以看到:

  1. Public IPv4 address 是空的
  2. Elastic IP addresses 也是空的
  3. IPv6 address 有值



設定 local ssh 連線 (不使用網頁 console 連線)

我習慣用 iTerm2 連線,所以這邊會設定 local ssh 連線。

如果可以接受只使用網頁 console 連線,設定 EC2 Instance Connect Endpoint 就可以了。


新增 EC2 Instance Connect Endpoint (eice)

主要參考 AWS 文件 [7]。

簡記步驟:

  1. VPC -> Endpoints -> Create Endpoint -> 選擇 EC2 Instance Connect Endpoint -> 選擇對應的 VPC、Subnet、Security Group

新增 user 與 policy

我新增了一個 IAM user ec2-connect

你可以自己另外命名,下面的內容會用 ${iam_user_name} 來代表這個 user 字串。

目前會用到的變數說明

  • ${region}: ec2 的 region,例如我放在東京 ap-northeast-1
  • ${account_id}: 你自己帳號的 id;可以在 aws 畫面的右上角點開自己的帳號名稱,然後複製 id
  • ${iam_user_name}: 我們剛才新增的 user name
  • ${pem_file}:你的私鑰 pem file 名稱

然後經過各種試錯以及文件,新增了以下 policy 套用在此 user 上:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SecureInstanceConnect",
            "Effect": "Allow",
            "Action": [
                "ec2-instance-connect:OpenTunnel",
                "ec2-instance-connect:SendSSHPublicKey",
                "ec2-instance-connect:SendSerialConsoleSSHPublicKey"
            ],
            "Resource": [
                "arn:aws:ec2:${region}:${account_id}:instance/*",
                "arn:aws:ec2:${region}:${account_id}:instance-connect-endpoint/*"
            ]
        },
        {
            "Sid": "DescribeInstanceConnectEndpoints",
            "Action": [
                "ec2:DescribeInstanceConnectEndpoints"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "EC2InstanceConnect",
            "Action": [
                "ec2:DescribeInstances"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Note

  1. 如果你需要更細緻的權限控管,可以自己調整 Resource 、或是新增 Condition



下載 AWS CLI、設定 profile

記得下載 v2 最新版本

並且設定好 config, profile。

cat ~/.aws/config

1
2
3
4
5
6

...

[profile ${iam_user_name}]
region = ${region}
output = text

(記得自己替換變數哦)


~/.aws/credential 設定 aws access credential:

1
2
3
4
5
...

[${iam_user_name}]
aws_access_key_id = ${your_access_key_id}
aws_secret_access_key = ${your_secret_access_key}
  • 記得權限要是 600

測試連線

根據文件 [3],需要透過 aws ec2-instance-connect open-tunnel 來連線。


先安裝 awscli;偷懶用 brew 比較快。

1
brew install awscli
  • 你可以參考官方安裝文件 [8]

我把我的 host alias 稱為 prod (你可以自己調整)

所以,在 ~/.ssh/config 裡面加入以下內容:

1
2
3
4
5
Host prod
   HostName ${instance_id}
   User ec2-user
   IdentityFile ~/.ssh/${pem_file}
   ProxyCommand /usr/local/bin/aws ec2-instance-connect open-tunnel --instance-id %h --profile ${iam_user_name}

Note

  1. 我預設安裝 aws CLI 的路徑是在 /usr/local/bin/aws,你可以自己調整路徑
  2. %h 會被替換成 HostName 的值,這裡就是 ${instance_id}

測試連線,終於成功!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ ssh prod

Last login: Sat May 11 05:10:07 2024 from 
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2025-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

如果有任何錯誤,可以打開 verbose 模式 debug:

1
2
3
4
ssh -v prod

# 更多訊息
ssh -vv prod

例如我一開始 policy 有少給 ec2:DescribeInstances,會出現以下錯誤:

1
2
3
4
5
6
7
8
An error occurred (UnauthorizedOperation) 
when calling the DescribeInstances operation: 
You are not authorized to perform this operation. 
User: arn:aws:iam::${account_id}:user/${iam_user_name} is not authorized to perform: ec2:DescribeInstances 
because no identity-based policy allows the ec2:DescribeInstances action

kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535

(有調整排版、替換我的 account id)

如果沒有開 verbose 模式,可能只會看到最後兩行的錯誤訊息:

1
2
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535



測試 DNS

新建 nginx container

用 docker 來建立一個 nginx container,方便快速測試 DNS

1
 docker run --name nginx --rm -p 80:80 -d nginx

Note

  • 如果你可以快速佈建服務、驗證是否有連通的話,就不用特別起 nginx 測試。

修改 route53 (或你的 DNS 服務商) record

這邊以 route53 為例,你可以去你的 DNS 服務商修改對應的 record:

  1. 新增一個 AAAA record,指向你的 EC2 IPv6 address

例如

Record Name        Type   Value
ipv6.example.com   AAAA   a4bf:0361:2649:ab9b:917c:91dd:41e5:f1dc

測試連線

等幾秒鐘,可以用 dig 驗證解析結果:

1
2
3
dig ipv6.example.com AAAA +short
# response
a4bf:0361:2649:ab9b:917c:91dd:41e5:f1dc

生效後,可以用 curl 來測試是否有連通 nginx:

1
$ curl ipv6.example.com

或是開瀏覽器輸入網址,應該可以看到 nginx 的歡迎畫面。


Note

  • 如果你發現 DNS 有解析,但是瀏覽器點開卻顯示:

找不到 ipv6.example.com 的 DNS 位址,正在診斷問題

你可以先嘗試用手機使用行動網路測試,這有可能是你的家裡 wifi 出去之後,ISP 那邊不支援 IPv6。

可以聯繫客服關切一下。


後記

當我發現家裡 ISP 不支援 IPv6 解析時,我就知道 EC2 還是得留一個 IPv4 的 Elastic IP

因為其他 user 可能還是會有一樣的狀況,總不能讓服務因為這個原因無法連線。

所以現在準備要去把 server 合併在一台比較划算,實在是夭壽。




REF

  1. https://aws.amazon.com/tw/events/taiwan/techblogs/aws-public-ipv4-address-charge/
  2. https://swordandsignals.com/2024/01/28/ec2-from-ipv4-to-ipv6.html
  3. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-using-eice.html
  4. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-with-ec2-instance-connect-endpoint.html
  5. https://repost.aws/questions/QUWOoCv-AJQ9ynUhKsB6HyQQ/i-need-restriction-access-for-the-specific-instance-for-the-aws-new-feature-called-secure-eic-connection
  6. https://docs.aws.amazon.com/zh_tw/AWSEC2/latest/UserGuide/permissions-for-ec2-instance-connect-endpoint.html
  7. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-ec2-instance-connect-endpoints.html
  8. https://docs.aws.amazon.com/zh_tw/cli/latest/userguide/getting-started-install.html

主題 StackJimmy 設計