【tfsec】Terraformの静的セキュリティスキャンを行ってみよう!

Terraformの、セキュリティスキャンツールである「tfsec」の触り部分を取り上げました。 簡単なデモもあるため、ぜひご参考にしていただければと思います。
2022.02.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。

今回は、tfsecを使用して、Terraformのセキュリティスキャンを体験してみようと思います。

結論

  • tfsecは、Terraformの静的なセキュリティスキャンを行うツール
  • ローカル環境で動作可能な認証情報などを用意しなくていい

tfsecとは

Aqua Security社の、オープンソースツールです。

ざっくり、ご説明すると、Terraformの静的なセキュリティスキャナーです。

ローカル、CIパイプライン環境で実行できるように設計されています。

いざ実践

インストール

今回は、Macでtfsecを使用してみようと思います。

% brew install tfsec

各OSのインストール方法は、こちらからご参照ください。

スキャン実行

今回、tfsec用に、以下のtfファイルを作成しました。

VPC、パブリックサブネット、ルートテーブル、セキュリティグループを作成します。

vpc.tf

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  # source  = "../modules/vpc"
  version = "3.12.0"
  # insert the 23 required variables here
  cidr = "10.0.0.0/16"
  name = "terraform-reintroduction"
  public_subnets = ["10.0.0.0/24", "10.0.1.0/24"]
  azs = ["ap-northeast-1a", "ap-northeast-1c"]
}


resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [module.vpc.vpc_cidr_block]
  }

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

  tags = {
    Name = "allow_tls"
  }
}

では、実行してみます。

tfファイルを保管している、ディレクトリ内に移動して、tfsecコマンドを実行してみます。

% tfsec

Result #1 CRITICAL Security group rule allows egress to multiple public internet addresses. 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 vpc.tf Line 30
───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   26  │   egress {
   27  │     from_port        = 0
   28  │     to_port          = 0
   29  │     protocol         = "-1"
   30  │     cidr_blocks      = ["0.0.0.0/0"]
   31  │     ipv6_cidr_blocks = ["::/0"]
   32  │   }
───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-vpc-no-public-egress-sgr
      Impact Your port is egressing data to the internet
  Resolution Set a more restrictive cidr range

  More Information
  - https://aquasecurity.github.io/tfsec/v1.1.5/checks/aws/vpc/no-public-egress-sgr/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


Result #2 CRITICAL Security group rule allows egress to multiple public internet addresses. 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 vpc.tf Line 31
───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   26  │   egress {
   27  │     from_port        = 0
   28  │     to_port          = 0
   29  │     protocol         = "-1"
   30  │     cidr_blocks      = ["0.0.0.0/0"]
   31  │     ipv6_cidr_blocks = ["::/0"]
   32  │   }
───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-vpc-no-public-egress-sgr
      Impact Your port is egressing data to the internet
  Resolution Set a more restrictive cidr range

  More Information
  - https://aquasecurity.github.io/tfsec/v1.1.5/checks/aws/vpc/no-public-egress-sgr/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


Result #3 LOW Security group rule does not have a description. 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 vpc.tf Lines 26-32
───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   26  │   egress {
   27  │     from_port        = 0
   28  │     to_port          = 0
   29  │     protocol         = "-1"
   30  │     cidr_blocks      = ["0.0.0.0/0"]
   31  │     ipv6_cidr_blocks = ["::/0"]
   32  │   }
───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
          ID aws-vpc-add-description-to-security-group-rule
      Impact Descriptions provide context for the firewall rule reasons
  Resolution Add descriptions for all security groups rules

  More Information
  - https://aquasecurity.github.io/tfsec/v1.1.5/checks/aws/vpc/add-description-to-security-group-rule/
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
  - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


  timings
  ──────────────────────────────────────────
  disk i/o             23.956709ms
  hcl parsing          74.834µs
  evaluation           21.812586ms
  adaptation           960µs
  running checks       4.643584ms
  total                109.267ms

  counts
  ──────────────────────────────────────────
  blocks               6
  modules              1
  files                9

  results
  ──────────────────────────────────────────
  ignored              0
  excluded             0
  critical             2
  high                 0
  medium               0
  low                  1


  3 potential problem(s) detected.

実行結果から、セキュリティグループ周りで、3件検知されていることが確認できます。

重要度について

tfsecでは、検知結果を4つの重要度でカテゴライズしています。

  • critical
  • high
  • medium
  • low

重要度の具体的な、選別方法は明記されていないですが、検知されたルールに、一通り目を通しておきましょう。

aws-vpc-no-public-egress-sgr

An egress security group rule allows traffic to /0.

Result #1, #2で、アウトバウンドルールが、フルオープンであることを検知していました。

リファレンスに、解決策や影響範囲が記載されているので、「何がどのように悪い」のか、とてもわかりやすいです。

aws-vpc-add-description-to-security-group-rule

Missing description for security group rule.

Result #3では、アウトバウンドルールに、説明書きが無いことを検知していました。

たしかに、説明書きがないと、使用用途がわかりづらいため、追加した方がいいですね。

ルール検知を無視する

スキャン実行で検知されたアウトバウンドルールのフルオープンを、ignore(無視)に変更してみようと思います。

変更方法は、コード内にコメントとして、検知を無視するコードを追記していきます。

無視を行うコードの有効範囲は、同一行と一つ下の行までです。

Result #1のみを無視する

vpc.tf

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [module.vpc.vpc_cidr_block]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    # tfsec:ignore:aws-vpc-no-public-egress-sgr
    cidr_blocks      = ["0.0.0.0/0"] # ignored
    ipv6_cidr_blocks = ["::/0"] # critical
  }

  tags = {
    Name = "allow_tls"
  }
}

/* or

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [module.vpc.vpc_cidr_block]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"] # tfsec:ignore:aws-vpc-no-public-egress-sgr
    # 一行開ける
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "allow_tls"
  }
}

*/

Result #1,2を無視する

vpc.tf

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [module.vpc.vpc_cidr_block]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"] # tfsec:ignore:aws-vpc-no-public-egress-sgr
    ipv6_cidr_blocks = ["::/0"] # ignored →コードの影響範囲は1行下まで及ぶため
  }

  tags = {
    Name = "allow_tls"
  }
}

/* or

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [module.vpc.vpc_cidr_block]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"] # tfsec:ignore:aws-vpc-no-public-egress-sgr
    ipv6_cidr_blocks = ["::/0"] # tfsec:ignore:aws-vpc-no-public-egress-sgr
  }

  tags = {
    Name = "allow_tls"
  }
}

*/

最後に

今回は、かなり簡単ではありますが、tfsecの使い方をご紹介しました。

より応用的な使い方は、別途ブログにできたらと思います。

以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!