TerraformでSecurity Groupを作ったら上手くいかなかった

TerraformでSecurity Groupを作成した際の私の失敗談です。皆さんも同じ轍を踏まないようお気をつけください。
2019.05.31

こんにちは、大阪オフィスのかずえです。
前回お伝えした通り、Terraformを使い始めました。

Terraform初心者が実戦投入するまでにやったこと

今回は、初めてのTerraform案件にて私がやってしまった失敗をお話しします。
初歩的な内容で恐縮ですが、以後同じ轍を踏む方がいらっしゃらないよう。。

やりたかったこと

踏み台インスタンス経由でprivateサブネットにあるインスタンスにRDP接続をすることです。(Windowsインスタンスです)

  1. 踏み台インスタンス用のSecurity Groupを作成します。
  2. publicサブネットに踏み台インスタンスを作成し、上記Security Groupをアタッチします。
  3. privateサブネットインスタンス用のSecurity Groupを作成します。送信元は上記踏み台用Security Group(がアタッチされているリソース)のみ許可します。
  4. privateサブネット上にインスタンスを作成し、上記Security Groupをアタッチします。

コード

抜粋していますが、だいたいこんな感じです。(TerraformのVersionは0.12です)

resource "aws_security_group" "bastion" {
  name        = "bastion-sg"
  description = "bastion-sg"
  vpc_id      = "(VPCのid)"

  ingress {
    from_port = 3389
    to_port   = 3389
    protocol  = "tcp"

    cidr_blocks = [
      "(自分のIP)",
    ]
  }
}

resource "aws_security_group" "app" {
  name        = "app"
  description = "app"
  vpc_id      = "(VPCのid)"

  ingress {
    from_port       = 3389
    to_port         = 3389
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion.id]
  }
}

resource "aws_instance" "bastion" {
  ami               = "ami-04d0f44a3c739f093"
  availability_zone = "ap-northeast-1a"
  instance_type     = "t2.micro"

  tags = {
    Name = "test-bastion"
  }

  vpc_security_group_ids = [aws_security_group.bastion.id]

  subnet_id = "(public subnetのid)"
  root_block_device {
    volume_size = 30
    volume_type = "gp2"
  }
}

resource "aws_instance" "app" {
  ami               = "ami-04d0f44a3c739f093"
  availability_zone = "ap-northeast-1a"
  instance_type     = "m5.large"

  tags = {
    Name = "test-app"
  }

  vpc_security_group_ids = [aws_security_group.app.id]

  subnet_id = "(private subnetのid)"
  root_block_device {
    volume_size = 30
    volume_type = "gp2"
  }
}

…何がマズイかお分かりになりましたでしょうか?

起きた不具合

  • オフィスから踏み台インスタンス(bastion)にはRDPできる(OK)
  • 踏み台インスタンスからprivateサブネットのインスタンス(app)にRDP接続できない!
    • appのSecurity Groupを全開放にしてみる → 接続できない!
    • appと同じ構成のインスタンスをpublic subnetに作成してみて踏み台から接続 → できない!
    • appのWindowsファイアウォールは停止しているので関係なさそう
    • NACLも設定していないから関係なさそう

原因

セキュリティグループのアウトバウンドの記述をしていなかったから。

はい。Terraformはセキュリティグループを作成する時、アウトバウンドのルールも明記する必要があります。egressという引数がそれです。

Terraform AWS provider Resource: aws_security_group

この設定が無い場合、作成されるSecurity Groupのアウトバウンドは全拒否になります

実際に上記tfファイルで作成されたSecurity Groupをコンソールで確認してみましょう。
踏み台インスタンス用Security Groupのアウトバウンドルール このように何もルールがありません。

ちなみに、コンソールで作った場合はどうなるかというと、
コンソールで作成した場合のデフォルトアウトバウンドルール このようにデフォルトで全許可のルールが設定されます。

今回の場合、踏み台インスタンスのSecurity Groupのアウトバウンドルールのせいで、踏み台インスタンスからappにRDP接続する際に、踏み台インスタンスから外に通信が出れていなかったわけです。
普段アウトバウンドのルールを設定することが無い(デフォルトの全許可のまま)のことが多いので、この部分を意識せずappインスタンス側の問題点がないかというところばかり調査していました。。

対処法

Security Groupリソース欄にegress引数を追加します!

resource "aws_security_group" "bastion" {
  name        = "bastion-sg"
  description = "bastion-sg"
  vpc_id      = "(VPCのid)"

  ingress {
    from_port = 3389
    to_port   = 3389
    protocol  = "tcp"

    cidr_blocks = [
      "(自分のIP)",
    ]
  }
  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }  
}

resource "aws_security_group" "app" {
  name        = "app"
  description = "app"
  vpc_id      = "(VPCのid)"

  ingress {
    from_port       = 3389
    to_port         = 3389
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion.id]
  }
  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }  
}

まとめ

という訳で、Terraformでセキュリティグループを作成する場合は、アウトバウンド(egress)も明示的に定義しましょう。しましょう。します。

参考リンク