「誰からの通信を許可するか」をコードで設計する
ここを理解すると、
«「なぜAWSは安全なのか」»
が、コードレベルで少しずつ分かるようになる。
今回は、前回作ったEC2 Security Groupを完成させる。
そして今日から少しずつ、
「AWSのサービスを作る」
から、
「誰に通信を許可するのかを設計する」
という実務寄りの考え方に進んでいく。
まず復習
現在の構成はこうなっている。
Internet
│
▼
Internet Gateway
│
▼
ALB
(ALB Security Group)
│
▼
Listener
│
▼
Target Group
│
▼
EC2
(EC2 Security Group)
前回作ったのは、
EC2 Security Group
だった。
EC2 Security Groupでは、
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [
aws_security_group.alb.id
]
}
として、
«ALB Security Groupから来るHTTP通信だけをEC2の80番ポートで受け付ける»
というルールを作った。
ここまではOK。
しかし、まだ考えることがある。
EC2にSSHしたい場合は?
EC2を運用していると、
管理者
↓
EC2
という通信をしたい場合がある。
例えばLinux EC2にSSH接続する場合、基本的には、
22番ポート
を使用する。
つまりSecurity Groupにも、
管理者PC
│
SSH(22)
│
▼
EC2 Security Group
│
▼
EC2
という通信を許可する必要がある。
でも「22番を全世界に公開」は危険
例えば、
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
としたらどうなるだろう?
"0.0.0.0/0" は、
すべてのIPv4アドレス
を意味する。
つまり、
世界中
↓
EC2:22
という状態になってしまう。
SSHの入口を全世界に公開することになるため、一般的には避けたい。
そこで「誰からの通信なのか」を指定する
例えば、
会社の固定IP
│
SSH(22)
│
▼
EC2
のように、
特定のIPアドレスからだけSSHを許可する
という設計にする。
Terraformでは、
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"203.0.113.10/32"
]
}
のように書ける。
ここで新しいCIDRが登場した。
203.0.113.10/32
では、この "/32" は何を意味するのだろう?
CIDRの復習
これまでにも、
10.0.0.0/16
や、
0.0.0.0/0
が登場した。
CIDRの "/数字" は、
ネットワークの範囲をどれくらい指定するか
を表している。
例えば、
0.0.0.0/0
なら非常に広い範囲。
一方、
203.0.113.10/32
は、
203.0.113.10
という1つのIPアドレスだけを指定する。
つまり、
203.0.113.10/32
なら、
«「このIPアドレスからの通信だけ許可する」»
という設計にできる。
今日の構成図
今回、新しく追加するのはここ。
管理者PC
│
SSH
22番
│
▼
EC2 Security Group
│
▼
EC2
全体で見ると、
Internet
│
▼
Internet Gateway
│
▼
ALB
(ALB Security Group)
│
▼
Listener
│
▼
Target Group
│
▼
EC2 Security Group
▲ │
│ ▼
管理者PC SSH(22) EC2
となる。
ここで重要なのは、
ALBからEC2への通信
と、
管理者PCからEC2へのSSH通信
では、
「誰を許可するか」の指定方法が違うこと。
今日コードを書く場所
今日は、
resource "aws_security_group" "ec2"
だけを修正する。
現在ある、
ALBからHTTP
ingress {
}
に加えて、
管理者PCからSSH
ingress {
}
を追加する。
イメージはこう。
resource "aws_security_group" "ec2" {
ALBからHTTP
ingress {
}
管理者PCからSSH
ingress {
}
外へ通信
egress {
}
}
完成版コード
今回の完成形はこちら。
resource "aws_security_group" "ec2" {
name = "ec2-sg"
vpc_id = aws_vpc.main.id
ALBからのHTTP通信を許可
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [
aws_security_group.alb.id
]
}
管理者PCからのSSH接続を許可
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"203.0.113.10/32"
]
}
EC2から外部への通信を許可
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [
"0.0.0.0/0"
]
}
}
コードを理解する
① Security Groupを作る
resource "aws_security_group" "ec2" {
EC2用のSecurity Groupを作成する。
② どのVPCに所属させるか
vpc_id = aws_vpc.main.id
作成するSecurity Groupを、
aws_vpc.main
のVPCに所属させる。
③ ALBからのHTTP通信
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [
aws_security_group.alb.id
]
}
これは、
«ALB Security Groupから来たTCP 80番の通信を許可する»
という意味。
ここではCIDRではなく、
security_groups = [
aws_security_group.alb.id
]
としている。
つまり、
ALB
│
│ HTTP :80
▼
EC2 Security Group
│
▼
EC2
という関係をSecurity Group同士で表現している。
④ 管理者PCからのSSH
次はこちら。
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"203.0.113.10/32"
]
}
これは、
«203.0.113.10というIPアドレスからのSSH通信だけを許可する»
という意味。
203.0.113.10
│
SSH :22
│
▼
EC2
という通信を許可している。
なぜALBはSecurity Groupで、SSHはCIDRなのか?
ここが今回の一番重要なポイント。
ALBはAWS上のリソースなので、
ALB
↓
ALB Security Group
↓
EC2 Security Group
というように、
Security Groupを使って通信元を表現できる。
一方、管理者PCはAWSの外にある。
管理者PC
↓
インターネット
↓
AWS
↓
EC2
となる。
そのため、
«「このSecurity Groupから来た通信を許可する」»
ではなく、
«「このIPアドレスから来た通信を許可する」»
という形で指定する。
そこで、
cidr_blocks = [
"203.0.113.10/32"
]
を使う。
今日の問題🔥
Q1
203.0.113.10/32
これは何を表していると思う?
Q2
なぜSSH(22番)は、
0.0.0.0/0
じゃダメなのだろう?
Q3【今日一番重要】
今まで、
ALB → EC2
の通信ではSecurity Groupを参照した。
しかしSSHでは、
cidr_blocks = [...]
を使った。
なぜ使い分けるのだろう?
今回の回答
Q1
«特定のCIDRからの通信を受ける。»
Q2
«0.0.0.0/0にしたら皆通信出来てしまうから。»
Q3
«EC2はALBからだけの通信を受け取りたかったからセキュリティグループを参照するだけで良かったが、SSHは特定の通信だけを通したいのでCIDRが必要。»
今回のレビュー🔥
Q1:90点
回答:
«特定のCIDRからの通信を受ける。»
方向性は正しい。
ただ、今回はもう一歩踏み込める。
203.0.113.10/32
の "/32" は、
1つのIPアドレスだけ
を表している。
つまり、
203.0.113.10/32
なら、
203.0.113.10
という1つのIPからの通信だけを許可する。
ここを理解できると、
/0 → 非常に広い範囲
/16 → 大きなネットワーク範囲
/24 → さらに限定されたネットワーク
/32 → 1つのIPアドレス
という感覚につながってくる。
90点!
Q2:100点
回答:
«0.0.0.0/0にしたら皆通信出来てしまうから。»
これはその通り。
0.0.0.0/0
は、
すべてのIPv4アドレス
を対象にする。
そのため、
世界中
↓
SSH :22
↓
EC2
という状態になってしまう。
SSHは管理用の入口なので、必要な送信元に絞ることが重要。
100点!
Q3:95点
回答:
«EC2はALBからだけの通信を受け取りたかったからセキュリティグループを参照するだけで良かったが、SSHは特定の通信だけを通したいのでCIDRが必要。»
かなり良い。
特に、
«「ALBからだけの通信を受け取りたかった」»
という部分が重要。
ただし、もう一段正確にすると、
ALB
↓
AWS上のリソース
↓
Security Groupで送信元を表現できる
なのに対して、
管理者PC
↓
AWSの外部
↓
Security Groupを指定できない
↓
IPアドレス/CIDRで送信元を指定する
という違いになる。
つまり、
「特定の通信だからCIDR」
というより、
「通信元を何で識別できるかによって、Security Group参照とCIDRを使い分けている」
と考えるとかなり強い。
95点!
コードレビュー
今回のコードでは、いくつか修正ポイントがあった。
① "security_groups" の "="
最初のコードでは、
security_groups [
となっていた。
Terraformでは、
security_groups = [
とする。
② "security_groups" の場所
最初は、
resource "aws_security_group" "ec2" {
security_groups = [
aws_security_group.alb.id
]
}
のようになっていた。
しかし、
security_groups
は今回の書き方では、
ALBからの通信を許可するingressルールの中
に入れる。
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [
aws_security_group.alb.id
]
}
ここはTerraformの構文として大事。
③ egressの "port"
最初のコードでは、
egress {
port = 0
port = 0
となっていた。
正しくは、
egress {
from_port = 0
to_port = 0
。
今回の完成形をもう一度
最終的にはこうなる。
resource "aws_security_group" "ec2" {
name = "ec2-sg"
vpc_id = aws_vpc.main.id
ALBからHTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [
aws_security_group.alb.id
]
}
管理者PCからSSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"203.0.113.10/32"
]
}
外部への通信
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [
"0.0.0.0/0"
]
}
}
今日の最重要ポイント
今回覚えたいのは、コードそのものではない。
これ。
誰から通信が来る?
│
├── AWS上のリソース
│ ↓
│ Security Group参照
│
└── 特定のIPアドレス
↓
CIDR
例えば、
Internet
│
▼
ALB
│
│ ALB Security Group
▼
EC2
なら、
security_groups = [
aws_security_group.alb.id
]
。
一方、
管理者PC
│
│ SSH :22
▼
EC2
なら、
cidr_blocks = [
"203.0.113.10/32"
]
。
「誰から来る通信なのか?」
を考えることで、Terraformのコードが決まってくる。
これが今回の大きな学び。
そして、今回かなり大事だったこと
今回のコードを書くのに、
1時間かかった。
でも、これは全然悪いことではない。
むしろ今回の内容は、
Security Group
CIDR
Ingress
Egress
ALB
EC2
SSH
を全部つなげて考える必要がある。
しかも今回は、
「このコードは何を書くのか?」
ではなく、
「この通信を誰に許可するべきか?」
を考えた。
ここから少しずつ、Terraformの勉強が、
Terraformの文法を覚える
↓
AWSの構成を理解する
↓
通信経路を考える
↓
必要なルールを設計する
↓
それをTerraformでコードにする
という流れに変わっていく。
これが実務でのインフラエンジニアの考え方に近づいていく部分。
今回の総合評価
項目| 評価
コード| 90点
Q1| 90点
Q2| 100点
Q3| 95点
総合| 🌟95点🌟
特にQ3はかなり良かった。
単純に、
«「CIDRはIPを書くもの」»
で終わらず、
«「ALBはSecurity Groupで指定できるけど、管理者PCはどうする?」»
というところまで考えられている。
ここから先は、Security Groupの設定を暗記するのではなく、通信関係から逆算してコードを書く練習に進んでいく。
今日からの授業スタイル
今回の学習で、
構成図 → 書く場所 → コード → レビュー
という流れがかなり相性が良いことが分かった。
今後もこの形式で進める。
① AWS構成図を見る
↓
② 今日どこを作るか確認
↓
③ 通信経路を考える
↓
④ Terraformを書く
↓
⑤ 自分の回答をレビュー
↓
⑥ AWS設計として理解する
Terraformをただ書けるようになるのではなく、
「構成図を見たら、必要なTerraformが頭の中で組み立てられる」
ところを目指していく。
そして最終的には、
構成図
↓
「この通信にはこのルールが必要だな」
↓
Terraform
↓
AWS
まで、自分でつなげられるようにする。
ここまで来ると、Terraformが「コードを書く勉強」から、
AWSの設計をコードに変換する勉強
に変わってくる。☁️🔥