Slack BotでAWSのセキュリティーグループ設定を行う話。

[ヴァル研究所 Advent Calendar 2016](http://qiita.com/advent-calendar/2016/val)、19日目はSlackからAWSのセキュリティグループの設定を追加・削除する例を紹介しようと思います。

## AWSのセキュリティグループ

EC2インスタンスを立ち上げてsshでログインする際、基本的には社内ネットワークからのみssh接続を受け付けるようなセキュリティグループの設定を行うかと思います。とはいえ、テレワークなどで自宅や近所のカフェからsshで接続するような場面もあります。
その都度セキュリティグループを設定すれば良いのですが、AWSのマネジメントコンソールから自分のEC2インスタンスに紐付いているセキュリティグループを見つけて設定を追加・削除するのはちょっと面倒です。

そこで今回は、SlackからAWSのセキュリティグループの設定を追加・削除するBotを作成してみました。

## セキュリティーグループ設定・削除Bot

Botのソースコードは以下のようになります。RubyスクリプトからAWS CLIのコマンドを呼び出す形でセキュリティグループの操作を行います。AWS CLIの応答には若干のタイムラグがあるので、スレッド上でコマンドを実行するのが良さそうです(Botの応答性を上げるという意味で)。
今回はssh(TCP/22)のインバウンドポートのみを設定・削除する動作になっています。

使い方は単純に`sg authorize <SGID> <CIDR>`でSG追加、`sg revoke <SGID> <CIDR>`でSG削除、`sg describe <SGID>`で現在のセキュリティグループでインバウンドの疎通が許可されているIPアドレス一覧の表示を行います。

```ruby
#!/usr/bin/env ruby
# coding: utf-8

require 'net/http'
require 'uri'
require 'erb'
include ERB::Util
require 'json'

require 'slack'

def post(channel, msg)
  param = {
    token:   ENV['SLACK_TOKEN'],
    channel: channel,
    text:    msg,
    username: "SecurityGroupBot",
    icon_url: 'https://ekiworld.net/ekiworld/info/images/machinami.png'
  }
  Slack.chat_postMessage(param)
end

Slack.configure {|config|
  config.token = ENV['SLACK_TOKEN']
}
client = Slack.realtime

start_time = Time.now.to_i

client.on :message do |data|
  post_time = data["ts"].sub(/\..*$/, "").to_i
  next if post_time < start_time

  msg = data["text"]

  # "sg <authorize|revoke> ..."が投稿された場合の処理。
  # sshのSGを追加、削除する。
  if msg =~ /^sg (.*) (.*) (.*)/
    type  = $1
    next if type != "authorize" and type != "revoke"

    sg_id = $2

    # XXX.XXX.XXX.XXX/XXという文字列は、Slack的に電話番号として
    # 認識され、先頭に"tel:"とか付けられてしまうので除去する。
    cidr  = $3.sub(/<tel:/, '').sub(/\|.*$/, '')
    next unless sg_id or cidr

    channel = data["channel"]
    if type == "authorize"
      post(channel, "SG追加します。")
    else
      post(channel, "SG削除します。")
    end

    cmd = ''
    cmd = cmd << "aws ec2 #{type}-security-group-ingress "
    cmd = cmd << "--group-id #{sg_id} "
    cmd = cmd << "--ip-permissions '[{\"IpProtocol\": \"TCP\", \"ToPort\": 22,\"FromPort\": 22,\"IpRanges\": [{\"CidrIp\": \"#{cidr}\"}]}]'"

    begin
      Thread.new do
        # AWS CLIコマンドを実行する。
        p JSON.parse(IO.popen(cmd, "r+") {|p| p.read})
      end
    rescue Exception => e
      puts e.message
      puts e.backtrace.inspect
    end
  end

  # "sg describe ..."が投稿された場合の処理。
  # 指定されたSGグループに設定されているIngress側IPアドレスを表示する。
  if msg =~ /^sg describe (.*)/
    sg_id = $1
    cmd = "aws ec2 describe-security-groups --group-ids #{sg_id}"

    channel = data["channel"]
    begin
      Thread.new do
        addr_list_str = ''

        # AWS CLIコマンドを実行する。
        json_str = JSON.parse(IO.popen(cmd, "r+") {|p| p.read})
        json_str["SecurityGroups"][0]["IpPermissions"][0]["IpRanges"].each do |iprange|
          addr_list_str = addr_list_str << iprange["CidrIp"] << "\n"
        end

        if addr_list_str != ''
          result = "セキュリティグループで許可されているIPアドレス\n\`\`\`\n#{addr_list_str}\`\`\`"
        else
          result = "セキュリティグループで許可されているIPアドレスはありません。"
        end

        post(channel, result)
      end
    rescue Exception => e
      puts e.message
      puts e.backtrace.inspect
    end

  end

end
STDERR.puts("ok.")

client.start
```

微妙に注意が必要な個所として、Slackでは`XXX.XXX.XXX.XXX/XX`のようなフォーマットを入力すると電話番号として解釈されるようで、Botには`<Tel:XXX.XXX.XXX.XXX/XX>`のようなフォーマットが追加された文字列が渡されてきます。
IPアドレスとして処理する際には、付加された文字の除去が必要になります。

## 実際に使ってみる

実際にBotを動かしてみます。`sg describe <SGID>`で現状のセキュリティグループで疎通許可されているIPアドレスが一覧表示されます。

![img100.png](https://qiita-image-store.s3.amazonaws.com/0/152217/7831f0fb-eb34-3bbe-a41a-be373dd0b1fa.png)

ここで`sg authorize <SGID> <CIDR>`と投稿すると、指定したIPアドレスがTCP/22(ssh)のインバウンド設定として追加されます。疎通許可IPアドレスの項目が増えていますね。

![img101.png](https://qiita-image-store.s3.amazonaws.com/0/152217/16440e75-affa-482c-0bf2-c3db6bd40307.png)

`sg revoke <SGID> <CIDR>`で設定したIPアドレスを除去できます。これで一連のセキュリティグループ設定の追加・削除が行えることが確認できました。

![img103.png](https://qiita-image-store.s3.amazonaws.com/0/152217/bf4c8533-ea7e-5125-27b2-72355b717530.png)

## まとめ

Slack BotからAWSのセキュリティグループ設定の追加・削除を行ってみました。サンプルなのでTCP/22(ssh)のインバウンド設定のみですが、出先で一時的にsshの疎通を許可したいようなケースで有用かなと思います。