はじめに
AWSアカウントのセキュリティを守るため、ログイン時に多要素認証する検討を行いましたが、簡単に多要素認証を実装するには、メンバーの私用スマホにアプリをインストールする必要がありました。私用スマホには入れるのはあまり好ましくないので、アプリをインストールせずにセキュリティ面を強化できると良い!ということで、IPアドレス制限をかけることにしました!
しかし、IPアドレス制限をかけていることで、リモート勤務するにあたり弊害があったので、解決方法を提案します!
状況整理
IAMユーザのIP制限
全てのIAMユーザにIP制限のIAMポリシーがアタッチされているため、決められたIPアドレスからしかログインができません。
アタッチされているIAMポリシーは以下の通りです。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"[オフィスのIPアドレス]/32"
]
}
}
}
]
}
自宅のIPアドレスが毎日変わる
コロナの影響で自宅勤務になりましたが、ポケットWi-FiのIPアドレスが毎日変わり、IAMポリシーで事前に自宅のIPアドレスを登録することができません。
期間限定
現状はコロナの影響での在宅勤務なので、期限はコロナが落ち着くまで。あんまりコストもかけたくない!
解決方法
IPアドレスを追加する処理とIAMポリシーをお掃除する処理の2つの処理を実行することにしました。
IPアドレス追加処理
自宅から許可したいIPアドレスを持たせてAPI Gatewayを叩きます。そのAPI GatewayをトリガーとしてLambdaを実行し、最新のIAMポリシーに先ほど持たせたIPアドレスを追加します。API Gatewayはフルオープンになっていますが、AWSへログイン時に認証情報が必要なので、そこでセキュリティは担保しています。
ポリシーお掃除処理
毎日IPアドレスが追加されるので、IAMポリシーをお掃除(リセット)するためのLambdaを定期実行させます。S3に保存してあるIPアドレス追加処理前のIAMポリシーオブジェクトを取得し、IAMポリシーを更新します。こちらはCloudWatch Eventsをトリガーとします。
IAMポリシーを更新するときに必要なこと
IAMポリシーは最大5つまでバージョンを保存することができます。コンソール上でIAMポリシーを更新する際には、保存されているバージョンのうち1つ削除することで、5つ以上増えないようになっています。しかし、Lambda関数でIAMポリシーを更新する際には、削除処理を実装しなければいけません。また、デフォルト と表記があるバージョンが使用されるIAMポリシーなので、新しく追加するIAMポリシーをデフォルトに設定する必要があります。
IP制限IAMポリシーの作成
今回は例として123.123.123.123
のIPアドレスのみを許可したIAMポリシーを作成しました。実際にはこのポリシーをユーザにアタッチしておきます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"123.123.123.123/32"
]
}
}
}
]
}
IPアドレス追加処理
IP制限IAMポリシーに、許可したいIPアドレスを追加します。
Lambda関数の作成
Lambda関数を作成していきます。
IAMポリシー
AWSにIP制限をかけているIAMポリシーに対しての以下の権限を付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:GetPolicyVersion",
"iam:ListPolicyVersions",
"iam:CreatePolicyVersion",
"iam:DeletePolicyVersion"
],
"Resource": "[IP制限IAMポリシーarn]"
}
]
}
このポリシーをアタッチしたIAMロールを作成し、Lambdaに紐づけてください。
ソースコード
golangで書いてみました。
ソースコード : https://github.com/Maika995/aws-sourceip/blob/master/add/main.go
API Gatewayの作成
GETメソッドを作成します。作成方法は下記の記事を参考にさせていただきました。
参考:AWS Lambdaにクエリ文字列(get値)を引数で渡すトリガを設定する
前述のLambdaのコードに合わせて、クエリとしてIPアドレスを持たせるため、メソッドリクエストは以下のように設定しました。
そして、統合リクエストのマッピングテンプレートはテンプレートが定義されていない場合を選択し、以下のテンプレートを入力しました。
{
"SourceIp": "$input.params('SourceIp')"
}
最後にリソースのデプロイを行い、発行されたURLをコピーしておきます。
実行結果
コピーしておいたURLにIPアドレスをクエリとして持たせてブラウザからたたきます。
ブラウザにはLambda関数の戻り値として設定していた文字列が返ってきたので成功したっぽいです。
IAMポリシーを確認すると…
無事先ほど指定したIPアドレスが許可されています!
これで、無事自宅からでもAWSにログインできるようになりました!
ポリシーお掃除処理
定期実行でIAMポリシーを元々のIAMポリシーに戻す、お掃除バッチの実装です。
S3の作成
元バージョンのIAMポリシーを保管するために、S3バケットを作成します。パブリックアクセスもすべてブロックしたバケットを作成し、先ほどのIP制限IAMポリシーをJSONファイルにしてアップロードします。
Lambda関数の作成
Lambda関数を作成していきます。
IAMポリシー
IAMポリシーに関する権限に加えて、S3のオブジェクトを取得する権限を付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[バケット名]/[オブジェクト名]"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"iam:ListPolicyVersions",
"iam:CreatePolicyVersion",
"iam:DeletePolicyVersion"
],
"Resource": "[IP制限IAMポリシーarn]"
}
]
}
このポリシーをアタッチしたIAMロールを作成し、Lambdaに紐づけてください。
ソースコード
golangで書いています。
ソースコード : https://github.com/Maika995/aws-sourceip/blob/master/clean/main.go
CloudWatch Events
夜中の0時に定期実行するように設定しました。
そしてお掃除Lambdaに紐付けます。
Cron式で設定します。UTCなので-9時間で表記します。
実行結果
0時をすぎて、IAMポリシーがリセットされていることが確認できました!
おわりに
注意事項
1回目の実行時は削除できるIAMポリシーのバージョンがないので、(デフォルトのバージョンは削除できません。)バージョンの削除処理の部分で409エラーが返ってきていますが、その他の処理は普通に実行されます。2回目以降はバージョンが2つ保存されているので、409エラーも出ることなく、古いバージョンが削除されるようになります。
ボツになった施策
第一弾は、以下のようにポリシーのバージョンの中で1番古いバージョン(3番)をお掃除処理に使用するバージョンにしていました。
- 最新ポリシー(デフォルト)
- 2番目に新しいポシリー
- お掃除に使用する、元々のIP制限ポリシー
両方の処理とも2番目に新しいポリシーを削除して更新します。お掃除処理は3つ目のポリシーを取得してそのまま使うようにしていましたが…ある日、リセットされてない!ってことに気づきました。3番のバージョンがなくなっていました。おそらく複数人が利用しているAWSなので、手動でIAMポリシーを変更したなど、無意識に3番のバージョンが削除されてしまったのかなと思います。そういう経緯もあり、いろいろな人が使うものなので、お掃除用のバージョンはS3にファイルを置くことにしました!
より良くするには
上記の設定であれば、API Gatewayはフルオープンでどこからでもたたくことができます。あまり手間をかけずセキュリティ面でより固くするなら、クエリの条件にパスワードとかを追加してみるのもいいかなと思います!あとは、APIキーとかを使ってみるのも良さそう。
感想
現在、在宅勤務なので実際に使っていますが問題なく自宅からAWSにログインができています!
これでAWSログイン問題解決しました!めでたしめでたし!
あと、golangを使う機会も少しずつ増えてきて、golangとSDKの苦手意識薄まってきたなあと感じました!