弊社では以前からAWS環境のEC2(Linux、Windows問わず)にリモートアクセスする際は、コンソールサーバ経由でブラウザ接続していました。ただ、現行のコンソールサーバからSSMセッションマネージャを経由するよう、Linuxサーバへのリモートアクセス運用を現在進行形で移行中です。 言わずと知れた機能ですが、ついでにSlack通知もできるようにしたため、ご紹介します。
SSMセッションマネージャの動作環境
Session Manager の使用開始 に色々と書いてありますが、本記事では準備が完了したEC2(CentOS 7.6)を使います。
- 対象サーバにSSMエージェントがインストール済み
- SSMセッションマネージャに対応するIAMポリシーが付与されていること
- 各サーバからはインターネット接続が可能であること
大雑把に言ってしまえば、上記の3つでしょうか。
IAMポリシーによる制御については、Session Manager の使用開始のステップ2に記載されています。
3点目を補足するなら、パブリックサブネットのサーバはもちろん、NATGW経由やPrivateLink経由でも、SSM機能は動作します。
SSMエージェントを導入しておけば、オンプレ機器に対してもセッションマネージャからログイン可能な点もメリットですね。
SSMセッションマネージャの設定
SSMセッションマネージャの数少ない設定内容は、ログデータの扱いだけです。 今回はS3にセッションログを出力しています。
Lambda関数の作成
SSMセッションの開始を検知してSlack通知するLambda関数です。 本記事では、CloudWatch Eventの「AWS API Call via CloudTrail」をトリガーに使用しました。
CloudWatch Event設定
検知したいイベント名は「StartSession」になります。 CloudWatch Event設定後のイベントパターン定義は次の通りです。
{ "source": [ "aws.ssm" ], "detail-type": [ "AWS API Call via CloudTrail" ], "detail": { "eventSource": [ "ssm.amazonaws.com" ], "eventName": [ "StartSession" ] } }
Lambda関数のコード
詳細は省略しますが、今回は下記情報を通知しています。
User: SSMセッションマネージャを使用したIAMユーザ。
SSMのセッションID自体にユーザ名が含まれているため、そこから抽出。Server: ログイン先のインスタンスID。
Session ID: 一意のSSMセッションID。
Slackへの通知内容は、以下のようになります。
今回作成したLambda関数のコードは、次の通りです。
下記コードの send_data
ではemojiも指定できるので、癒されたい絵文字をお持ちの方は是非使ってください。
import json import urllib.request from datetime import datetime, date, timedelta def notice_slack(start,user,target,id): msg = ''' Start Time: %s User: %s Server: %s Session ID: %s ''' % (start,user,target,id) send_data = { "username": "ssm-session_start", "text": msg, } send_text = "payload=" + json.dumps(send_data) request = urllib.request.Request( "https://hooks.slack.com/services/~~~", data=send_text.encode("utf-8"), method="POST" ) with urllib.request.urlopen(request) as response: response_body = response.read().decode("utf-8") def lambda_handler(event, context): start_s = event['time'] start_t = datetime.strptime(start_s, '%Y-%m-%dT%H:%M:%SZ') + timedelta(hours=9) print(start_t) start = datetime.strftime(start_t, '%Y-%m-%dT%H:%M:%SJST') target = event['detail']['requestParameters']['target'] id = event['detail']['responseElements']['sessionId'] user = id[0:(len(id) - 18)] print(start) print(user) print(target) print(id) notice_slack(start,user,target,id)
SSMセッションマネージャ経由のログイン
「ターゲットインスタンス」を選択後、「セッションの開始」をクリックすると、ターミナルがブラウザの新規画面(タブorウィンドウ)で開かれます。ログインユーザは「ssm-user」で、ログインシェルは sh のようです。
ちなみに、Githubでわりに紛糾しているissueで、
Custom Shell for aws ssm start-sessionがあります。
現在、コメント数は第2位。。ssm-userのログインシェルなんてどーでもいい、とは思えないギークな人が多いのです。
なお、コメント数が一番多いのは、「コピペするとセッションマネージャが遅い」というissueです。
最後に
SSMセッションマネージャはブラウザベースですが、SSH接続であることに変わりはありません。
ただ、セキュリティグループ等のインバウンド通信要件が不要であり、リモートアクセスの監査ログも楽できます。弊社では以前から、EC2へのリモートアクセスにApache Guacamoleを使ってきましたが、Linuxサーバに関しては順次、SSMセッションマネージャ方式に移行しています。
※Windows OS等のGUI環境への接続では、今後もApache Guacamoleで構築したコンソールサーバに頼っています
ただ、ブラウザベースでサーバにログインできる分、それなりの考慮点はでてきます。 例えば次の点などがあります。
- AWSコンソールの認証方式が重要 →弊社ではOneloginでカバーしています。 →IAMポリシーでは、必要に応じてSSMセッション先を制限することも可能です。
- SSMセッションの「ssm-user」が制限なしのsudo権限を持っている →「1つのサーバを複数OSユーザで運用を使い分けたい」という要望など、実運用に合わないケースもあります。 →/etc/sudoers.d/ssm-agent-users が自動生成されている影響です。例として、以下のように変更すればOSユーザを運用で使い分け可能になります。
##差分のみ表示 #ssm-user ALL=(ALL) NOPASSWD:ALL ssm-user ALL=(ALL) NOPASSWD:/usr/bin/su - test01 ssm-user ALL=(ALL) NOPASSWD:/usr/bin/su - test02