LoginSignup
48
24

More than 5 years have passed since last update.

terraform で aws_security_group の更新を安全に行う

Last updated at Posted at 2016-10-27

ダメなパターン

これを

resource "aws_security_group" "test" {
    name        = "test"
    vpc_id      = "${aws_vpc.test.id}"
    ingress {
        from_port       = 22
        to_port         = 22
        protocol        = "tcp"
        cidr_blocks     = ["x.x.x.x/32"]
    }
}

こう変えると

 resource "aws_security_group" "test" {
     name        = "test"
     vpc_id      = "${aws_vpc.test.id}"
     ingress {
         from_port       = 22
         to_port         = 22
         protocol        = "tcp"
-        cidr_blocks     = ["x.x.x.x/32"]
+        cidr_blocks     = ["x.x.x.x/32","y.y.y.y/32"]
     }
 }

ログが次のように出て、なんか diff が怪しい。1回 ingress を全部消して、再度全部作ってるような。

aws_security_group.test: Modifying...
  ingress.2293169953.cidr_blocks.#:     "1" => "0"
2016/10/27 18:33:14 [DEBUG] apply: aws_security_group.test: executing Apply
  ingress.2293169953.cidr_blocks.0:     "x.x.x.x/32" => ""
  ingress.2293169953.from_port:         "22" => "0"
  ingress.2293169953.protocol:          "tcp" => ""
  ingress.2293169953.security_groups.#: "0" => "0"
  ingress.2293169953.self:              "0" => "0"
  ingress.2293169953.to_port:           "22" => "0"
  ingress.3838495408.cidr_blocks.#:     "0" => "2"
  ingress.3838495408.cidr_blocks.0:     "" => "x.x.x.x/32"
  ingress.3838495408.cidr_blocks.1:     "" => "y.y.y.y/32"
  ingress.3838495408.from_port:         "" => "22"
  ingress.3838495408.protocol:          "" => "tcp"
  ingress.3838495408.security_groups.#: "0" => "0"
  ingress.3838495408.self:              "" => "0"
  ingress.3838495408.to_port:           "" => "22"

TF_LOG=DEBUG をつけて実行すると以下のようにログが出て、Revoke & Authorize している感じ。

2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: 2016/10/27 18:33:15 [DEBUG] Revoking security group {
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   Description: "Managed by Terraform",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   GroupName: "test",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpPermissions: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       FromPort: 22,
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       IpProtocol: "tcp",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       IpRanges: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:           CidrIp: "x.x.x.x/32"
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:         }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       ToPort: 22
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: } ingress rule: []*ec2.IpPermission{{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   FromPort: 22,
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpProtocol: "tcp",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpRanges: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       CidrIp: "x.x.x.x/32"
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   ToPort: 22
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: }}
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: 2016/10/27 18:33:15 [DEBUG] Authorizing security group {
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   Description: "Managed by Terraform",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   GroupName: "test",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpPermissions: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       FromPort: 22,
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       IpProtocol: "tcp",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       IpRanges: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:           CidrIp: "x.x.x.x/32"
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:         }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       ToPort: 22
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: } ingress rule: []*ec2.IpPermission{{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   FromPort: 22,
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpProtocol: "tcp",
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   IpRanges: [{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       CidrIp: "x.x.x.x/32"
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:     },{
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:       CidrIp: "y.y.y.y/32"
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws:   ToPort: 22
2016/10/27 18:33:15 [DEBUG] terraform-provider-aws: }}

ソースコードを読むと https://github.com/hashicorp/terraform/blob/11b3b7cf29fa4120974d293458beac47e1eedd32/builtin/providers/aws/resource_aws_security_group.go#L552-L559

        // TODO: We need to handle partial state better in the in-between
        // in this update.

        // TODO: It'd be nicer to authorize before removing, but then we have
        // to deal with complicated unrolling to get individual CIDR blocks
        // to avoid authorizing already authorized sources. Removing before
        // adding is easier here, and Terraform should be fast enough to
        // not have service issues.

とあり、TODO になっている。Terraform は充分に早いからサービスISSUEは起こらないんじゃないかな(意訳)とのことだが ...

安全安心なパターン

aws_security_group_rule を使う

 resource "aws_security_group" "test" {
     name        = "test"
     vpc_id      = "${aws_vpc.test.id}"
 }

 resource "aws_security_group_rule" "test_rule_1" {
     security_group_id = "${aws_security_group.test.id}"
     type = "ingress"
     from_port = 22
     to_port = 22
     protocol = "tcp"
     cidr_blocks = ["x.x.x.x/32"]
 }

+resource "aws_security_group_rule" "test_rule_2" {
+    security_group_id = "${aws_security_group.test.id}"
+    type = "ingress"
+    from_port = 22
+    to_port = 22
+    protocol = "tcp"
+    cidr_blocks = ["y.y.y.y/32"]
+}

これなら追加だけになる。ログは以下。

2016/10/27 18:40:29 [DEBUG] apply: aws_security_group_rule.test_rule2: executing Apply
aws_security_group_rule.test: Creating...
  cidr_blocks.#:            "" => "1"
  cidr_blocks.0:            "" => "y.y.y.y/32"
  from_port:                "" => "22"
  protocol:                 "" => "tcp"
  self:                     "" => "0"

2016/10/27 18:40:30 [DEBUG] terraform-provider-aws: 2016/10/27 18:40:30 [DEBUG] Authorizing security group sg-66d6231b Ingress rule: {
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   FromPort: 22,
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   IpProtocol: "tcp",
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   IpRanges: [{
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:       CidrIp: "y.y.y.y/32"
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   ToPort: 22
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws: }
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws: 2016/10/27 18:40:30 [DEBUG] Found rule for Security Group Rule (sgrule-3952629376): {
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   FromPort: 22,
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   IpProtocol: "tcp",
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   IpRanges: [{
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:       CidrIp: "x.x.x.x/32"
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:     },{
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:       CidrIp: "y.y.y.y/32"
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:     }],
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws:   ToPort: 22
2016/10/27 18:40:30 [DEBUG] terraform-provider-aws: }

注意点は、from_port, to_port が同じだからと cidr_blocks の配列要素に追加しようとすると、destory & add になってしまう点。つまり、以下はダメ。aws_security_group_rule を別途用意すべき。

 resource "aws_security_group" "test" {
     name        = "test"
     vpc_id      = "${aws_vpc.test.id}"
 }

 resource "aws_security_group_rule" "test_rule_1" {
     security_group_id = "${aws_security_group.test.id}"
     type = "ingress"
     from_port = 22
     to_port = 22
     protocol = "tcp"
-    cidr_blocks = ["x.x.x.x/32"]
+    cidr_blocks = ["x.x.x.x/32","y.y.y.y/32"]
 }

セキュリティグループの瞬断について考察 (追記)

セキュリティグループの ingress または egress からエントリを削除すると新規接続が弾かれるようになるが、すでに開いている接続が切られるわけではないことを確認した。

なので、「ダメなパターン」でも Revoke してから Authorize するまでの短い時間に新規接続が来たorをする場合にのみ問題となる。TCPであれば 少し待って(Linuxならデフォルト1秒)再接続するので、この少しを許容できるかという点と、UDPの送信失敗を許容できるかという点が論点になるだろう。

まぁ、aws_security_group_rule を使っておくほうが安全というのは間違いない。

48
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
24