ようへいの日々精進XP

よかろうもん

1 つの AWS アカウントで出来るだけうっかりミスを抑えつつ本番環境を運用する為にやったこと

tl;dr

先日, 完全に自分のうっかりで AWS リソースをオペミスで壊してしまいサービス障害を発生させてしまいました. 原因は, 何を隠そう自分の「うっかり」であり, 完全に人為的なミスです. 今後, このような事故を出来るだけ起こさないように対応策を施したので書ける範囲で共有させて頂きます.

原因

AWS アカウントを利用して検証中の事故でしたが, 以下のような幾つかの状況が重なった事故だったと考えています.

  • 検証環境のリソースと本番環境のリソースが混在している状況だった
  • 検証環境のリソースと本番環境のリソースで見分けがつけ辛い状況だった
  • 利用している IAM ユーザーについて, 全てのリソースを無条件で操作出来る権限が付与されているアカウントだった
  • オペレーター (自分) がボーッとしてしまい, 特になんの疑いも持たずに操作してしまった, しかも, 一人で操作していた

これらの原因を受けて, 以下のような対策を実装しました.

実装

2 つの実装

  1. IAM ユーザーの分割: 通常, 検証等で使う IAM ユーザーと本番環境を操作出来る IAM ユーザーを分ける (通常使うアカウントでは本番環境のリソースは操作出来ないようにする)
  2. Chrome Extension による IAM ユーザーの判別: 本番環境が操作可能な IAM ユーザーで操作する場合, 自身のアカウントが本番環境を操作可能な IAM ユーザーであることを意識付けるようにする

実装 (1) IAM ユーザーの分割 〜 本番環境のリソース操作を拒否する 〜

従来は個人用として 1 つの IAM ユーザーを作成し, これを利用していました. この IAM ユーザーには管理者権限 (IAM ロールの AdministratorAccess 相当) がアタッチされていましたが, 今後は, この IAM ユーザーについては, 本番環境のリソースについて, 再起動や停止, 編集等の権限については無効するようなポリシーを追加しました.

具体的には, 以下のような IAM ポリシーを付与しました. 以下は, Environment タグ名に Production という値が設定されている EC2 インスタンスの再起動をはじめタグ操作を Deny しています.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "ec2:RebootInstances",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:CreateTags",
        "ec2:DeleteTags",
        "ec2:Terminate*"
      ],
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringEquals": {
          "ec2:ResourceTag/Environment": "Production"
        }
      }
    }
  ]
}

また, 今回の事故は具体的に申し上げると, 本番環境の RDS をフェイルオーバーさせてしまいました. ということで, RDS についても同様に Environment タグ名に Production という値が設定されている RDS クラスタインスタンスについても構成変更等が行えないように設定しました.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "rds:Modify*",
                "rds:Start*",
                "rds:Stop*",
                "rds:Reboot*",
                "rds:AddTagsToResource",
                "rds:RemoveTagsFromResource",
                "rds:FailoverDBCluster"
            ],
            "Resource": "arn:*:rds:*:*:cluster:*",
            "Condition": {
                "StringEquals": {
                    "rds:cluster-tag/Environment": "Production"
                }
            }
        },
        {
            "Effect": "Deny",
            "Action": [
                "rds:Modify*",
                "rds:Start*",
                "rds:Stop*",
                "rds:Reboot*",
                "rds:AddTagsToResource",
                "rds:RemoveTagsFromResource",
                "rds:FailoverDBCluster"
            ],
            "Resource": "arn:*:rds:*:*:db:*",
            "Condition": {
                "StringEquals": {
                    "rds:db-tag/Environment": "Production"
                }
            }
        }
    ]
}

これらの拒否ポリシーは, 先述の Administrator Access 相当のロールが付与されていても, それを上書きするように作用する為, 当該 IAM ユーザーアカウントについては, 従来どおり検証用で作成したリソースを自由に操作しつつ, うっかり本番環境 (Environment:Production が付与された) リソースを操作してしまうようなことを防ぐことが可能になります.

ただ, 立場上, 本番環境を操作する必要がある為, これに対応する為に本当に全てのリソースを操作可能な IAM ユーザーアカウントを作成しました. 本番環境を操作するような場合にはアカウントを切り替えて (もしくは, ブラウザを切り替えて) 操作することになります. 多少の運用負担にはなってしまいますが, うっかり操作で障害を発生させてしまった際の影響と比較すると大したことではないと考えています.

実際に拒否ポリシーが適用された IAM ユーザーで EC2 を停止しようとすると, 下図のようにエラーとなります.

f:id:inokara:20200131002140p:plain

以下, IAM ポリシーで利用可能な, アクションやリソース, 条件コンテキストキーのリンクです. これらの条件を利用することで, タグ以外の条件でもリソース操作を制限することが可能かと思われます.

実装 (2) Chrome Extension による IAM ユーザーの判別

先述の通り, 立場上, 全てのリソースに対する権限が付与された IAM ユーザーアカウントも運用することになりますが, この IAM ユーザーアカウントでマネジメントコンソールにログインした際には, 下図のようにヘッダ付近に現在のユーザーが管理者ユーザーである旨のメッセージが出力されるようにしました.

f:id:inokara:20200130234935p:plain

これは, 以下の記事を参考にさせて頂き, オレオレ Chrome Extension を作りました.

qiita.com

コード自体は以下のように数行レベルのものです.

$(function() {
    console.log($(".nav-elt-label").text());
    var sp = /super|administrator|admin/;
    if($(".nav-elt-label").text().match(sp)) {
        $("#h").after('<div style="background-color:#FF0000;text-align:center;"><h3>全てのリソースを操作出来る IAM ユーザーでアクセスしています.  Production 環境のリソースの取り扱いに注意しましょう.</h3></div>');
    }
});

マネジメントコンソールのヘッダ部分を解析して, 特定の文字列が含まれていたらヘッダ部分に HTML を差し込みます. 上記の例だと, IAM ユーザーに superAdministrator という文字列が含まれている状態を想定しています. 当初は, リソース名に production という文字列が含まれていたら, 何か警告を出すような仕込みを入れてみようと頑張ってみましたが, マネジメントコンソールの処理を解析出来る程の能力があるわけもなく, 上記のような対応にしました.

まずは, この対応で頑張ってみたいと思います.

最後に

このような場合, そもそも検証環境と本番環境で AWS アカウントを分けるというのが常套手段なのかもしれませんが, こういう対応も可能ということを共有させて頂ければ思います.また, この対応で完全にミスが防げるというわけではありません. あくまでも, ミスによる障害を抑制する為の保険の一つでしかありません.

以上, 事故の起きないインフラ環境を目指して精進したいと思います.