kakakakakku blog

Weekly Tech Blog: Keep on Learning!

CircleCI と Piculet でセキュリティグループを CI する

管理コンソールでセキュリティグループ設定を管理するのは限界があると思っていて,例えば以下のような状態になってしまうことがあると思う.

  • 必要に応じて日々増えていくルール設定(無意識にポチポチと追加してしまう)
  • 使って無さそうだから消したいけど影響が出たら嫌だから残しているルール設定(何のために追加したのか情報が無い)
  • 無駄に広く開放されてしまっているルール設定(動作確認のために取り急ぎ設定したまま残ってしまう)

そこで,セキュリティグループ設定もバージョン管理できれば良さそうだと考えて,AWS Community Hero の @sgwr_dts さんが開発した Piculet を検証している.Piculet 以外にも Codenize.tools に AWS のリソースを管理するツールが多く公開されていて,素晴らしすぎる!

github.com

インストール

簡単!

$ gem install piculet

エクスポート

簡単!

$ piculet --export --output Groupfile
Export SecurityGroup to `Groupfile`

追加で --split オプションを付けると VPC ごとに Groupfile を分割できる.もし複数 VPC を運用してる場合だと便利な機能だと思う.

$ piculet --export --output Groupfile --split
Export SecurityGroup
  write `./vpc-xxxxxxxx.group`
  write `Groupfile`
$ cat Groupfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'vpc-xxxxxxxx.group'

また --names オプションでグループ名を指定すると対象のセキュリティグループを固定することができる.以下は試しに default グループをエクスポートしてみた.

$ piculet --export --output Groupfile --names default
Export SecurityGroup to `Groupfile`
$ cat Groupfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
ec2 "vpc-xxxxxxxx" do
  security_group "default" do
    description "default VPC security group"

    ingress do
      permission :any do
        groups(
          "default"
        )
      end
    end

    egress do
      permission :any do
        ip_ranges(
          "0.0.0.0/0"
        )
      end
    end
  end
end

Groupfile

DSL で表現されているため,直感的に理解できて可読性が高い.

  • ingress : インバウンド
  • egress : アウトバウンド
  • permission : プロトコル&ポート
  • groups : 送信元(セキュリティグループ)
  • ip_ranges : 送信元(IP アドレス)

次に「1.2.3.4/32 から http & https」と「グループ名 "default" から ssh」を許可する設定をしてエクスポートしてみた.正しくエクスポートできている.

# -*- mode: ruby -*-
# vi: set ft=ruby :
ec2 "vpc-xxxxxxxx" do
  security_group "sample-web" do
    description "sample-web"

    ingress do
      permission :tcp, 22..22 do
        groups(
          "default"
        )
      end
      permission :tcp, 80..80 do
        ip_ranges(
          "1.2.3.4/32"
        )
      end
      permission :tcp, 443..443 do
        ip_ranges(
          "1.2.3.4/32"
        )
      end
    end

    egress do
      permission :any do
        ip_ranges(
          "0.0.0.0/0"
        )
      end
    end
  end
end

JSON サポート

--format=json を追加すると JSON 形式で Groupfile をエクスポートできる.Groupfile をツールで処理する場合などは DSL より JSON の方が良さそう.

$ piculet --export --output Groupfile --names default --format=json
Export SecurityGroup to `Groupfile`
{
  "vpc-xxxxxxxx": {
    "sg-11111111": {
      "name": "default",
      "description": "default VPC security group",
      "tags": {
      },
      "owner_id": "111111111111",
      "ingress": [
        {
          "protocol": "any",
          "port_range": null,
          "ip_ranges": [

          ],
          "groups": [
            {
              "id": "sg-11111111",
              "name": "default",
              "owner_id": "111111111111"
            }
          ]
        }
      ],
      "egress": [
        {
          "protocol": "any",
          "port_range": null,
          "ip_ranges": [
            "0.0.0.0/0"
          ],
          "groups": [

          ]
        }
      ]
    }
  }
}

強制上書き

--export の挙動は強制上書きとなっていて,例えば「管理コンソール上の設定」と「Groupfile」がコンフリクトしているような場合,強制的に「管理コンソール上の設定」がエクスポートされる.まぁ実際に稼働している設定だし,適切な挙動だと思う.

CircleCI で CI をできるように

生成した Groupfile を GitHub で管理すれば,セキュリティグループ設定をバージョン管理することができる.さらに Groupfile の更新が形骸化しないように CircleCI で CI できたら良いなと思って試した.

事前準備

以下の環境変数を CircleCI 側に登録しておく.

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_REGION

IAM は CircleCI 用に作成した.CI するだけなら以下で良さそう.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt0000000000000",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeTags",
                "iam:GetUser"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

circle.yml

Piculet に diff 系のオプションは無かったため,CircleCI 上で diff 結果を判定するようにした.

machine:
  timezone:
    Asia/Tokyo
  ruby:
    version: 2.3.1

dependencies:
  pre:
    - gem install piculet

test:
  post:
    - piculet --export --output Groupfile.circleci
    - diff Groupfile Groupfile.circleci

結果

良さ!

f:id:kakku22:20161120013448p:plain

気になったところ

IAM ポリシーで iam:GetUser を許可する場合と許可しない場合で DSL の groups のエクスポート結果が異なっていた.仕様だとは思うけど,実際にコードを読んで原因を探ってみたいなと思う.

iam:GetUser 許可

「許可」の場合は「グループ名」になった.

ingress do
  permission :any do
    groups(
      "default"
    )
  end
end

iam:GetUser 非許可

「非許可」の場合は「グループ ID」になった.

ingress do
  permission :any do
    groups(
      ["111111111111", "sg-11111111"]
    )
  end
end

まとめと次回

今回は Piculet を使って以下を試してみた.セキュリティグループ設定をバージョン管理できるの最高!ただ,もう少し README にオプションの説明など,詳細なドキュメントが書かれてたら良いのになーと思った.

  • セキュリティグループ設定をエクスポートした
  • CircleCI で CI をできるようにした

さらに Piculet は --apply オプションで実際に反映することもできる.次回は --apply を試す!

関連記事

Mackerel の監視ルールを CircleCI で CI する話を前に書いた.インフラ系の設定値をバージョン管理して,プルリクエストを通してレビューできる開発プロセスはとにかく便利!

kakakakakku.hatenablog.com