ヴァル研究所 Advent Calendar 2016、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アドレス一覧の表示を行います。
#!/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アドレスが一覧表示されます。
ここでsg authorize <SGID> <CIDR>
と投稿すると、指定したIPアドレスがTCP/22(ssh)のインバウンド設定として追加されます。疎通許可IPアドレスの項目が増えていますね。
sg revoke <SGID> <CIDR>
で設定したIPアドレスを除去できます。これで一連のセキュリティグループ設定の追加・削除が行えることが確認できました。
まとめ
Slack BotからAWSのセキュリティグループ設定の追加・削除を行ってみました。サンプルなのでTCP/22(ssh)のインバウンド設定のみですが、出先で一時的にsshの疎通を許可したいようなケースで有用かなと思います。