この記事は every Tech Blog Advent Calendar 2024 の 6 日目の記事です。
はじめに
エブリーTIMELINE開発部の内原です。
全社的にSSHの利用を中止することができたので、そのような意思決定をすることに至った経緯や、その後の状況について紹介します。
なお前提として、下記記事はAWSに限定した内容となっています。
エブリーではGCP(GCE)も一部のサービスで利用しているのですが、GCEについては下記で説明する問題の影響がなかったため対象外としています。
SSH利用を中止したい理由
以下のような理由から、運用的にいろいろ辛い部分があったためです。
脆弱性対応で疲弊する
一般的にSSHサーバとしてOpenSSHが用いられることが多いと思いますが、このソフトウェアには時折セキュリティ脆弱性の問題が見つかることがあります。この脆弱性については放置できないケースも多いので、その都度工数が発生します。
今年だと CVE-2024-6387 の問題がありました。
共有アカウントにおけるセキュリティリスク
キーペアを用いてEC2にログインするケースなど共有アカウントでログインする運用では、退職者であってもログインできてしまうリスクがあります。
また共有アカウントの運用では、監査の観点でも誰がなにをしたかについても追跡が難しくなります。
個別アカウントでの運用は大変
かといって、ユーザ個別のアカウント運用を行うのはわりと面倒です。
手動で管理するのは当然として、なんらか外部サービスと連携してアカウント管理を自動化するアプローチであっても、面倒なことには変わりありません。
セキュリティグループ運用が面倒
SSHを使うためにはSSHポート番号(22番)を開放する必要がありますが、この管理方法についても考慮すべきことが多いです。
- ポートは全体開放(0.0.0.0/0)するか?
- 全体開放しないならどういう運用で開放するか?
- 管理コンソールで担当者が直接更新するか? なんらかIaCツールを用いるか?
- IPアドレスが頻繁に変わる場合はどうするか?
SSH利用を中止した後の代替手段
上記のようにSSHを利用し続けることは無視できないリスクがあると考えたため、SSHの利用を全社的に中止することにしました。
ただそうは言っても、現状の運用でSSHを利用しているケースも存在していたため、代替手段を用意する必要がありました。
EC2へのログインにSession Managerを利用する
AWS Systems Manager Session Managerを利用することでSSHの代替を行うことができます。 Session ManagerはEC2インスタンスに対してSSHの代替となるリモートシェルを提供するサービスです。
最近のEC2インスタンスならば通常SSM Agentは起動していますが、数年以上前に作成したインスタンスの場合はSSM Agentが起動していないことがあるため、その場合はSSM Agentを手動でインストールする必要があります。
また、インスタンスIAMロールには AmazonSSMManagedInstanceCore
ポリシーがアタッチされている必要があります。
EC2インスタンスを利用しないアプローチ
もしくは、踏み台用のEC2インスタンスを用いるのではなく、ECS Fargate Taskを都度起動するアプローチを採ることも可能です。以前にその対応を行った記事がありますので、参考にしてください。
RDS踏み台サーバをよく見かけるECS Fargate+PortForward+Adhocな機構に変更する
実際のSSH利用例と代替手段
EC2インスタンスへのログインを行なっているケース
EC2インスタンスにログインしてなんらかシェル操作を行なっているようなケースです。その場合は以下のようなコマンドでリモートシェルを利用することができます。
対応前
$ ssh -i path/to/key.pem $ec2_user@$ec2_host sh-5.2$
対応後
$ aws --profile $profile ssm start-session --target $instance_id Starting session with SessionId: foo.bar@nrcazkfv3a6gkcmdmihy7i4pbq sh-5.2$
ローカル環境からのRDSへの接続用Proxyとして利用しているケース
RDSのインスタンスはVPC内に存在するため直接接続することができないので、SSH Port Forwardingを利用してリモート接続するようなケースです。
例えば以下のようなコマンドでローカル環境からmysqlサーバに接続することができます。
対応前
$ ssh -L 3306:$remote_db_host:3306 $ec2_user@$ec2_host
$ mysql -h 127.0.0.1 -u$db_user -p $db_name Enter password: mysql>
対応後
このようなケースについても、AWS Systems Manager Session Managerのポート転送を利用することで代替することができます。
$ aws --profile $profile ssm start-session --target $instance_id \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters '{"host":["YOUR-REMOTE-DB-HOST"],"portNumber":["3306"],"localPortNumber":["3306"]}'
$ mysql -h 127.0.0.1 -u$db_user -p $db_name Enter password: mysql>
全社の状況把握と方針策定
状況把握
まずは全社で利用している全EC2インスタンスのリストを作成し、それぞれのインスタンス利用状況を可視化することにしました。
その際は以下のようなコマンドで一覧化したものをスプレッドシートに書き出し、担当部署を割り当てて部署ごとに利用状況を記載してもらいました。
$ aws ec2 describe-instances | jq -r '.Reservations[] as $r | $r.Instances[] | select(.State.Name!="terminated") | [$r.OwnerId, .InstanceId, (.Tags // [] | from_entries.Name // "NoName"), (.SecurityGroups[0].GroupName // "NoName"), .LaunchTime, .InstanceType, .State.Name] | @tsv'
上記コマンドによって以下のような出力を得られます。
AccountName OwnerId InstanceId TagName SecurityGroupName LaunchTime InstanceType State.Name ************ i-***************** INSTANCE-NAME SECURITY-NAME YYYY-MM-DDThh:mm:ss+00:00 INSTANCE-TYPE running
対象としたインスタンス数は全社で70個ほどで、これを担当する複数の部署に割り当てました。
なお、部署によってはインフラ構成が大きく異なっているケースもあり、管轄する個数にはだいぶ偏りがある状態でした。(ちなみに自分が所属しているTIMELINE開発部では該当するインスタンスは存在しませんでした)
方針策定
各部署では以下のいずれかの方針を選択してもらうことにしました。
インスタンスの削除
stopping状態のままになっているインスタンスやすでに利用していないインスタンスなど、削除しても問題ないインスタンスについては削除することにします。
SSHポート番号閉鎖
本来はSSHサーバ自体を停止するのが望ましいのですが、EC2の機構上動作しているインスタンスからSSHを無効化するのが難しかったため、SSHポート番号の閉鎖で対応することにしました。
ポート番号が塞がれていれば事実上外部からSSHで攻撃されるリスクは考慮しなくてよくなると考えたためです。
その後の状況
最初にリストを作成してから1ヶ月半ほどで、全部署での対応が完了しました。
前述の通り部署によって対象個数に偏りがあったため最終的にはそれなりの時間がかかることになりましたが、各部署のご協力あって無事完了させることができました。
上記対応を行った結果、現在は全社的にSSHの利用が中止され、セキュリティ上のリスクは大幅に軽減されました。
また、今後新たにEC2インスタンスを起動する場合にも同様の対処が行われるよう、全社的な運用ルールを別途定める予定です。
まとめ
SSHの利用を中止することで、セキュリティ上のリスクを軽減することができました。またSSHのアカウント管理に関する煩雑さもなくなり、運用コストの削減にもつながりました。さらに運用ルールを定めて、今後ともにセキュリティを維持していくことが重要と考えています。
以上、全社的にSSHの利用を中止するために行った取り組みについて紹介しました。