本記事は
NRIネットコム Advent Calendar 2022
2日目の記事です。
🎁 1日目
▶▶本記事 ▶▶
3日目
🎄
寒冷の候、皆様におかれましてはますますご清祥のこととお慶び申し上げます。
ということで、10ヶ月ぶりにこの記事(SSM Session Managerを使った踏み台サーバ構築)のその後を書きたいと思っております小林です。
実は前回の記事、公開前後にこんなやり取りがありました。
最終的に記事にはこんなこと書きましたが設定を強制できたんですね。
一応補足ですが、.aws/credentials、.aws/configに記載するスイッチロールの設定に role_session_name を指定することでセッション名を固定化することは可能です。 ただし設定実施は利用者に委ねられるので、運用に向いていないことには変わりません。
そして、ぱっとこんな提案出てくるとはさすがAPN Ambassadorですね。心強い。
なお上の画像内の参考リンクはこちらです。(クラスメソッドさんいつもお世話になっております。)
さて、構築した踏み台サーバなんですが、ちょうど需要もあって正式に共用の踏み台サーバとして採用される運びとなりました。
今回は正式採用にあたり加えた変更とネットコムのセキュリティの考え方についてお話ししようと思います。
ネットコムのセキュリティの考え方
ここでは踏み台サーバに関する「本番環境にアクセスする際に考慮すべきセキュリティ事項」について書きます。
(もちろん詳細をすべて書くわけにはいかないのでざっくり概要まで。)
- 構築が完了し、ユーザに利用されるために公開された環境を本番環境と定義する
- 本番アクセスは接続元を信頼性の高いとこに制限すること
- 誰が、いつ、接続したかを記録すること
- 誰が本番作業してる人がわかるようにすること
- 勝手に本番作業ができないようにすること
かいつまんで言うと、ユーザさんが使う環境は決められた場所から履歴を残して不正に触れないようにしようってことですね。
どうやって実現するか
先に述べたルール、どうやって実現したかを紹介していきます。
本番アクセスは接続元を信頼性の高いとこに制限すること
これは前回の記事で対応済みですね。
IAMポリシーでIPアドレスで接続元拠点を絞っています。
誰が、いつ、接続したかを記録すること
前回の記事記載のとおり、踏み台に接続するにはSSM Session Managerを使う必要があります。
さらに、冒頭に書いた「role_session_name」の設定を強制しています。
具体的には下記のような内容を信頼ポリシーに入れて、RoleSessionNameとIAMユーザー名がイコールでないとAssumeRoleできないようにしています。
(クラスメソッドさんの記事から引用)
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::222222222222:root" }, "Action": "sts:AssumeRole", "Condition": { "StringLike": { "sts:RoleSessionName": "${aws:username}" } } } ] }
つまり、セッション名にIAMユーザー名を入れて接続する必要があり、接続すると必ずCloudTrailに履歴が残る仕組みになっているので、「誰が、いつ、接続したかを記録」することができるようになっています。
誰が本番作業してる人がわかるようにすること(勝手に本番作業ができないようにすること)
これには少し工夫が必要でした。
テレワーク下でも「今この人が本番作業はじめたな」と気付く仕組みが必要です。
「勝手に本番作業ができないようにする」とありますが、作業を始めたことが他人にわかる仕組みを作ることで無断での作業の抑止力となります。
これにはCloudTrailのサブスクリプションフィルターを使い、StartSessionが検知されたときにSlack通知を行うLambdaを起動させることを考えました。
CloudTrail証跡をCloudWatch Logsに設定し、フィルタパターンに下記を設定します。
{ ($.eventName = "StartSession") && ($.requestParameters.target = "i-xxxxxxxxx(踏み台サーバのインスタンスID)") }
こをトリガーに下記のようなLabmdaを実行させます。
require 'json' require 'base64' require 'zlib' require 'net/http' require 'uri' require 'date' require 'time' def lambda_handler(event:, context:) json_log_data = JSON.parse(Zlib::GzipReader.new(StringIO.new(Base64.decode64(event["awslogs"]["data"]))).read) log_events = json_log_data['logEvents'] log_events.each do |log_event| message = JSON.parse(log_event['message']) userId = message['userIdentity']['principalId'] startDate = message['eventTime'] startDateJST = Time.parse(startDate) + (60 * 60 * 9) sourceIp = message['sourceIPAddress'] instanceId = message['requestParameters']['target'] sendMsg = "" sendMsg = sendMsg + userId.match(/.*:(.*)/)[1] + "が踏み台サーバへ接続しました。\n" sendMsg = sendMsg + "接続開始日時 :" + startDateJST.strftime("%Y/%m/%d %H:%M") + "\n" sendMsg = sendMsg + "接続元IPアドレス :" + sourceIp + "\n" sendMsg = sendMsg + "接続先インスタンスID:" + instanceId + "\n" # Slack送信 postUrl = "https://hooks.slack.com/services/XXXXXXXXX(SlackのImcomingWebhook URL)" postParams = { 'username': "接続通知", 'icon_url': 'https://xxxxxxxxxxxxxx' } uri = URI.parse(postUrl) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true req = Net::HTTP::Post.new(uri.path) req["Content-Type"] = "application/json;charset=UTF-8" postParams["text"] = sendMsg req.body = postParams.to_json res = http.request(req) end end
このLambdaによって接続したことがSlackに通知されるので、責任者が「認識済みだよ」のリアクションをする運用にしています。
Windowsサーバもほしいという声が!
さてさて、ここまでやったところで次の要望が…
作業に使うツールの関係でWindowsの踏み台サーバがほしいと…
どうしようかなと思いましたが、SSM Session Managerにはポートフォワード機能があるので、それを使うことでWindowsのRDPに対応できることに気付きました。
Linux踏み台のように普段のSSHと変わらない操作感とはいきませんが、
- SSM Session ManagerでWindows踏み台サーバへポートフォワード確立
- RDPクライアントでlocalhostのポートフォワード先へ接続
という形で簡単に接続することができます。
なんといってもこれまでに設定したSSM周りの設定をそのまま使うことができ、ポートフォワード確立を行った時点でSlackへ通知を飛ばすこともできます!
ということでSSMを使っていたことで、こちらの要望にも難なく応えることができたのでした。
まとめ
もともと自分の興味から試してたことなんですが、ちょうど需要もあり、やったことが実務にきれいに反映される形となりました。
部署内ではこういったどこかで役立ちそうな検証は歓迎されているので、こういった事例がどんどん出てくるとうれしいですね。
ちなみにこの記事のアイキャッチにしているこの画像は踏み台サーバにログインしたときに表示されるようmotdに設定しているものです。
こういった遊び心も大事。
さて、今後の踏み台の運用なのですが、せっかくSSMを使ってるのでPatch Managerを使ったパッチ適用やDocumentを利用したAnsibleプレイブックの実行などいろいろ便利な機能も導入していきたいなと思っています。
まだまだ改善の余地があるのでいろいろ試していきたいのですが、今のところは若手のお勉強タスクとして引き継きたいなと思っているので一旦私の検証はここまで。
この先は引き継ぎ先の誰かがまた記事にしてくれるかもしれません。
俺たちの運用はまだ始まったばかりだぜ!
(小林先生の次回作にご期待ください)