TerraformでセキュリティグループIDをソースに指定したセキュリティグループを作成する

セキュリティグループを作成するときに、インバウンドのソースとして別のセキュリティグループIDを指定する方法はとても便利です。

よく使うのは、AWSで踏み台サーバを用意してアプリケーションサーバにはその踏み台サーバ経由でしかsshできないようにする場合です。
踏み台サーバには特定の(社内などの)IPからしsshできないようなセキュリティグループを設定し、アプリケーションサーバにはそのセキュリティグループIDがソースであるsshのみを許可するようにします。
CIDRブロック単位だと許可範囲が広いですし、IPだと変更があったときにセキュリティグループの設定も修正する必要があるので、セキュリティグループIDをソースに指定するやり方は便利です。

さて、この方法でセキュリティグループを作成するにあたってTerraformを使おうとした場合、少し悩みました。

以降のTerraformはv0.12.12で動作確認しています。

仮に踏み台サーバはどこからでもsshできて大丈夫だとして、そのようなセキュリティグループを作成する場合は以下のようなTerraformになります。

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_security_group" "bastion" {
  name        = "bastion"
  description = "Security Group for bastion"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

これはこれで問題ないのですが、今回作成したセキュリティグループをソースに指定してセキュリティグループを作成する方法に少し悩みました。

公式によると、aws_security_groupリソースのingressブロックで取りうる要素は以下の通りで、それっぽい指定箇所はありません。 www.terraform.io

要素 説明
cidr_blocks (Optional) List of CIDR blocks.
ipv6_cidr_blocks (Optional) List of IPv6 CIDR blocks.
prefix_list_ids (Optional) List of prefix list IDs.
from_port (Required) The start port (or ICMP type number if protocol is "icmp")
protocol (Required) The protocol. If you select a protocol of "-1" (semantically equivalent to "all", which is not a valid value here), you must specify a "from_port" and "to_port" equal to 0. If not icmp, tcp, udp, or "-1" use the protocol number
security_groups (Optional) List of security group Group Names if using EC2-Classic, or Group IDs if using a VPC.
self (Optional) If true, the security group itself will be added as a source to this ingress rule.
to_port (Required) The end range port (or ICMP code if protocol is "icmp").
description (Optional) Description of this ingress rule.

というわけで、よくよく調べると、aws_security_group_ruleリソースで指定ができるようです。 www.terraform.io

要素 説明
source_security_group_id (Optional) The security group id to allow access to/from, depending on the type. Cannot be specified with cidr_blocks and self.

最終的に、以下のようになります。

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_security_group" "bastion" {
  name        = "bastion"
  description = "Security Group for bastion"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "application" {
  name        = "application"
  description = "Security Group for application"

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_security_group_rule" "application_from_bastion" {
  type                     = "ingress"
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.bastion.id
  from_port                = 22
  security_group_id        = aws_security_group.application.id
}