Snykを使用したIaC脆弱性診断をやってみた

## 0. はじめに
Snyk社が次世代の脆弱性管理ツール[Snyk(スニーク)](https://go.snyk.io/jp.html)を展開されています。私はこれまで当該ツールを使用したこともなければ、Snykという言葉自体を聞いたこともありませんでした。ですが、Snyk社のQiita Advent Calender 2023の案内メールをいただいたことがきっかけで、今回初めてSnykを使用してIaCの脆弱性診断をやってみました。本記事では、脆弱性診断及び診断に基づくIaCの改善例をご紹介します。

## 1. ツールの導入
脆弱性管理ツールSnykの利用においては、お試し程度であれば無料で使用することが可能です。また、サインアップの方法にはいくつかあるようですが、下記の画面を見ると、GitHubかGoogleアカウントを保有していれば、それでサインアップすることが可能でした。
![Snyk画面1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/69291892-e07e-8c9e-8765-9fa44255c637.jpeg)
GitHub上でコード管理されていればGitHubからこのツールを実行することもできるみたいですが、私は自分のPC内でIaCを管理しているので、PCにSnyk CLiをダウンロードして使用することにしました。私はWindows11 PCで使用する想定のため、Windows用のCLiをダウンロードしました。ダウンロードにあたっては下記を参考にしてください。
```
C:\Users\testuser>curl https://static.snyk.io/cli/latest/snyk-win.exe -o snyk.exe
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 89.7M  100 89.7M    0     0  27.1M      0  0:00:03  0:00:03 --:--:-- 27.2M
```
次にsnyk authコマンドを使用してPC認証を行います。その際、再度サインアップ処理が必要になりました。私はGitHubアカウントでサインアップしました。以下はコマンド実行時のサンプル出力です。
```
C:\Users\testuser>snyk auth

Now redirecting you to our auth page, go ahead and log in,
and once the auth is complete, return to this prompt and you'll
be ready to start using snyk.

If you can't wait use this url:
https://app.snyk.io/login?token=716b305c-1fd5-41f6-ae8e-dab17e2d3051&utm_medium=cli&utm_source=cli&utm_campaign=CLI_V1_PLUGIN&utm_campaign_content=1.1253.0&os=windows_nt&docker=false


Your account has been authenticated. Snyk is now ready to be used.
```
最終的に、「Your account has been authenticated. Snyk is now ready to be used.」というメッセージが出力されれば認証完了です。

## 2. IaCの脆弱性診断を実行
今回は、下記のAWS環境をプロビジョニングするIaCに対して脆弱性診断を実施してみます。
・東日本リージョンにCIDRアドレス範囲(10.0.0.0/16)をもつVPCを作成
・VPCの中にCIDRアドレス範囲(10.0.1.0/24)をもつパブリック・サブネットを作成
・外部からSSH接続(Port:22)、HTTPアクセス(Port:80)を許可するセキュリティー・グループを作成
・パブリック・サブネットにEC2インスタンスを1つ作成

下記のSnykポータルのIaC設定画面を見ると、AWSについてはCloudFormationとTerraformだけが対応しているようです。Ansibleについては未対応のようでした。
![Snyk画面2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/2d67f396-2147-c821-a22a-71214b23db8f.jpeg)

上記で記載したAWS環境をプロビジョニングするサンプルIaCについて、今回はTerraformのコードに対して脆弱性診断を実施してみました。

診断を行う対象のサンプルコードは下記のCreate_EC2_Old.tfになります。
```tf:Create_EC2_Old.tf
terraform {
  required_providers {
    aws  = {
        source = "hashicorp/aws"
        version = ">= 5.25.0"
    }
  }
}

locals{
  az_location = "ap-northeast-1a"
}

resource "aws_vpc""test"{
  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = "true"
  enable_dns_support = "true"
  tags = {
    Name = "My-VPC"
  }
}

resource "aws_subnet" "pub-sub"{
  vpc_id = aws_vpc.test.id
  cidr_block = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone = local.az_location
  tags = {
    Name = "My-Public-Subnet"
  }
  depends_on = [ 
    aws_vpc.test 
  ]
}

resource "aws_internet_gateway" "gw"{
  vpc_id = aws_vpc.test.id
  tags = {
    Name = "My-Internet-Gateway"
  }
  depends_on = [ 
    aws_vpc.test  
   ]
}

resource "aws_route_table" "public-rtbl"{
  vpc_id = aws_vpc.test.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
  tags = {
    Name = "My-Public-Rtbl"
  }
  depends_on = [ 
    aws_internet_gateway.gw
   ]
}

resource "aws_route_table_association" "att-pubtbl"{
  route_table_id = aws_route_table.public-rtbl.id
  subnet_id = aws_subnet.pub-sub.id
}

resource "aws_security_group" "secgrp"{
  name = "Public Security Group"
  description = "Security Group for Public Instance"
  vpc_id = aws_vpc.test.id
  ingress{
    description = "Permit SSL Access"
    cidr_blocks = ["0.0.0.0/0"]
    protocol = "tcp"
    from_port = 22
    to_port = 22
  }
  ingress{
    description = "HTTP Access"
    cidr_blocks = ["0.0.0.0/0"]
    protocol = "tcp"
    from_port = 80
    to_port = 80
  }
  tags = {
    Name = "My-Public-Security-Group"
  }
}

resource "aws_network_interface" "my_nic" {
  subnet_id = aws_subnet.pub-sub.id
  security_groups = [aws_security_group.secgrp.id]
}

resource "aws_key_pair" "my_key"{
  key_name = "MyEC2Key"
  public_key = file("~/.ssh/MyEC2Key.pub")
  tags = {
    Name = "My-Key"
  }
}

resource "aws_instance" "my_instance"{
  credit_specification {
    cpu_credits = "standard"
  }
  instance_type = "t2.micro"
  key_name = aws_key_pair.my_key.key_name
  ami = "ami-078296f82eb463377"
  network_interface {
    device_index = "0"
    network_interface_id = aws_network_interface.my_nic.id
  }
  tags = {
    Name = "My-Public-Instance"
  }
}

output "instance_publicip_addr"{
  value = aws_instance.my_instance.public_ip
}

```
当該IaCファイルの存在するフォルダに移動して、コマンドプロンプトから下記のコマンドを実行します。
```
snyk iac test --report --org=xxxxxxxx-yyyyyy-zzzzzz-tttttt
```
orgオプションに指定する値は、Snykの「Settings」→「General 」と遷移し、「Organization ID」と表示されている箇所がありますので、その内容をコピーします。
![Snyk画面3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/510b18ff-f08b-6388-7f64-9b18584688f7.jpeg)
コマンド実行後、下記のような診断結果が表示されます。単に診断結果の項目を表示するだけでなく、Critical、High、Medium、Lowと4段階のレベルに対する項目を表示してくれますので、診断結果を元に対応する項目に優先度を付けて取捨選択することが可能になります。
```
Snyk Infrastructure as Code

√ Test completed.

Issues

Low Severity Issues: 3

  [Low] EC2 API termination protection is not enabled
  Info:    To prevent instance from being accidentally terminated using Amazon
           EC2, you can enable termination protection for the instance. Without
           this setting enabled the instances can be terminated by accident.
           This setting should only be used for instances with high availability
           requirements. Enabling this may prevent IaC workflows from updating
           the instance, for example terraform will not be able to terminate the
           instance to update instance type
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-AWS-426
  Path:    resource > aws_instance[my_instance] > disable_api_termination
  File:    create_EC2_Old.tf
  Resolve: Set `disable_api_termination` attribute  with value `true`

  [Low] Public IPs are automatically mapped to instances
  Info:    Instances launched in this subnet will automatically have public IP
           assigned. Instances will be potentially accessible over public
           internet, which may lead to unauthorized access
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-AWS-427
  Path:    resource > aws_subnet[pub-sub] > map_public_ip_on_launch
  File:    create_EC2_Old.tf
  Resolve: Set `map_public_ip_on_launch` attribute  with value `false`

  [Low] EC2 instance accepts IMDSv1
  Info:    Instance Metadata Service v2 is not enforced. Metadata service may be
           vulnerable to reverse proxy/open firewall misconfigurations and
           server side request forgery attacks
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-130
  Path:    resource > aws_instance[my_instance] > metadata_options
  File:    create_EC2_Old.tf
  Resolve: Set `metadata_options.http_tokens` attribute to `required`

Medium Severity Issues: 3

  [Medium] Security Group allows open ingress
  Info:    That inbound traffic is allowed to a resource from any source instead
           of a restricted range. That potentially everyone can access your
           resource
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-1
  Path:    input > resource > aws_security_group[secgrp] > ingress[0]
  File:    create_EC2_Old.tf
  Resolve: Set `cidr_block` attribute with a more restrictive IP, for example
           `192.16.0.0/24`

  [Medium] Security Group allows open ingress
  Info:    That inbound traffic is allowed to a resource from any source instead
           of a restricted range. That potentially everyone can access your
           resource
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-1
  Path:    input > resource > aws_security_group[secgrp] > ingress[1]
  File:    create_EC2_Old.tf
  Resolve: Set `cidr_block` attribute with a more restrictive IP, for example
           `192.16.0.0/24`

  [Medium] Non-Encrypted root block device
  Info:    The root block device for ec2 instance is not encrypted. That should
           someone gain unauthorized access to the data they would be able to
           read the contents.
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-53
  Path:    resource > aws_instance[my_instance] > root_block_device > encrypted
  File:    create_EC2_Old.tf
  Resolve: Set `root_block_device.encrypted` attribute to `true`

-------------------------------------------------------

Test Summary

  Organization: xxxxxxxxxxxxxxx
  Project name: terraform

✔ Files without issues: 0
✗ Files with issues: 1
  Ignored issues: 0
  Total issues: 6 [ 0 critical, 0 high, 3 medium, 3 low ]

-------------------------------------------------------

Report Complete

  Your test results are available at: https://snyk.io/org/xxxxxxxxxx/projects
  under the name: terraform

```
これらの診断結果の中で、今回はMediumレベルの「EC2インスタンスのルート・ブロック・デバイスが暗号化されていない」と言われている項目に対し改善対応を行います。Resolve：の行に対応するためのパラメータ値まで記載していてくれるので便利です。


## 3 診断結果にもとづくIaCの修正対応
resource "aws_instance" "my_instance"{}の構文の中に、下記のステートメントを追加します。
```
 root_block_device {
    encrypted = true
  }
```

その後、ファイル名をCreate_EC2_New.tfにリネーム後、再度Iac診断コマンドを発行します。
```
snyk iac test --report --org=xxxxxxxx-yyyyyy-zzzzzz-tttttt
```
下記の出力結果から、Mediumレベルの診断結果の項目数が1つ解消されたことがわかります。「ルート・ブロック・デバイスが暗号化されていない。」との警告も表示されなくなりました。
```
Snyk Infrastructure as Code

√ Test completed.

Issues

Low Severity Issues: 3

  [Low] EC2 API termination protection is not enabled
  Info:    To prevent instance from being accidentally terminated using Amazon
           EC2, you can enable termination protection for the instance. Without
           this setting enabled the instances can be terminated by accident.
           This setting should only be used for instances with high availability
           requirements. Enabling this may prevent IaC workflows from updating
           the instance, for example terraform will not be able to terminate the
           instance to update instance type
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-AWS-426
  Path:    resource > aws_instance[my_instance] > disable_api_termination
  File:    create_EC2_New.tf
  Resolve: Set `disable_api_termination` attribute  with value `true`

  [Low] Public IPs are automatically mapped to instances
  Info:    Instances launched in this subnet will automatically have public IP
           assigned. Instances will be potentially accessible over public
           internet, which may lead to unauthorized access
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-AWS-427
  Path:    resource > aws_subnet[pub-sub] > map_public_ip_on_launch
  File:    create_EC2_New.tf
  Resolve: Set `map_public_ip_on_launch` attribute  with value `false`

  [Low] EC2 instance accepts IMDSv1
  Info:    Instance Metadata Service v2 is not enforced. Metadata service may be
           vulnerable to reverse proxy/open firewall misconfigurations and
           server side request forgery attacks
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-130
  Path:    resource > aws_instance[my_instance] > metadata_options
  File:    create_EC2_New.tf
  Resolve: Set `metadata_options.http_tokens` attribute to `required`

Medium Severity Issues: 2

  [Medium] Security Group allows open ingress
  Info:    That inbound traffic is allowed to a resource from any source instead
           of a restricted range. That potentially everyone can access your
           resource
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-1
  Path:    input > resource > aws_security_group[secgrp] > ingress[0]
  File:    create_EC2_New.tf
  Resolve: Set `cidr_block` attribute with a more restrictive IP, for example
           `192.16.0.0/24`

  [Medium] Security Group allows open ingress
  Info:    That inbound traffic is allowed to a resource from any source instead
           of a restricted range. That potentially everyone can access your
           resource
  Rule:    https://security.snyk.io/rules/cloud/SNYK-CC-TF-1
  Path:    input > resource > aws_security_group[secgrp] > ingress[1]
  File:    create_EC2_New.tf
  Resolve: Set `cidr_block` attribute with a more restrictive IP, for example
           `192.16.0.0/24`

-------------------------------------------------------

Test Summary

  Organization: xxxxxxxxxxxxx
  Project name: terraform

✔ Files without issues: 0
✗ Files with issues: 1
  Ignored issues: 0
  Total issues: 5 [ 0 critical, 0 high, 2 medium, 3 low ]

-------------------------------------------------------

Report Complete

  Your test results are available at: https://snyk.io/org/xxxxxxxxxx/projects
  under the name: terraform
```
当該診断結果は、上記のhttpsで始まるURLにアクセスして、terraformと表示されている部分をクリックすることで、同じ内容を参照することが可能です。
![Snyk画面5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/8bb25c66-226e-fb0d-7e2f-6d2ce4aad657.jpeg)


## 4. おわりに
今回は、Snyk社の次世代脆弱性管理ツールSnykを使用して、IaCの脆弱性診断、診断結果にもとづくコードの修正対応を実施する流れを紹介しました。SnykはIaCだけでなく、プログラム・ソース等についても診断することが可能です。今後もコードを書いたら、Snykを使用してよりセキュアなIaCやプログラムを書いていくようにしていきたいと思います。