AWSで目指した理想のCI/CDを別視点で考察してみる(データ保護観点)

はじめに

前回のブログでは、マルチアカウントにおけるIAMユーザーの設計戦略についてご紹介しました。 今回は少しテーマを変え、以前筆者がJAWS DAYS 2020で登壇させていただいたCI/CDの内容を基に、データ保護の観点からの設計~実装を取り上げたいと思います。

※少々お硬い内容を含みますが、AWS CI/CDセキュリティを考える上で一つのポイントになるはずなので、ご興味をお持ちの方は是非お付き合いください。m(_ _)m

前回ご紹介したCI/CD内容のおさらい

JAWSDAYS2020にて「金融サービス向けに理想のCI/CDを追い求めたお話」というタイトルで、筆者が担当するサービスのCI/CD設計をご紹介いたしました。 ここで、「理想」という点についてもう一度振り返ると、それは「CI/CD導入により期待すること」と、「業務特性として守らなければならないこと」の両立でした。

f:id:iselegant:20200609225939p:plain

高アジリティとアプリケーション開発注力の体制を実現しつつも、セキュリティや運用・統制、サービス品質を保証するためにたどり着いたCI/CD構成として、以下をご紹介しました。

f:id:iselegant:20200609230202p:plain

AWS Codeシリーズを活用し、コンテナベースのアプリケーションをデプロイまで持っていく流れです。

※今回必要なトピック箇所にスポットを当てるため、構成図から一部の内容を省略しています。 また、初見だと少し理解が追いつかない点もあるかもしれません。詳細は以下のスライドをご参照いただければと思います。

金融サービス向けに理想のCI/CDを追い求めたお話 - Speaker Deck

金融システムが守るべきデファクトスタンダード

現在、筆者は金融系のサービスを主軸に担当しています。 この金融系という枠組みで「業務特性として守らなければならないこと」の一つに該当するのが、金融情報システムセンター(通称、「FISC」)が刊行している金融機関等コンピュータシステムの安全対策基準・解説書(以降、「FISC安全対策基準」と呼びます)への遵守です。

FISC 金融情報システムセンター:FISCの概要

すごく簡単に述べれば、FISC安全対策基準は「金融系の情報システムを導入・運用する際に遵守すべきガイドライン集」です。 FISC安全対策基準は昭和60年に初版が刊行されてから改定が続けられています。最新版である「第9版:令和2年3月改訂版」では、大きく分けて統制・実務・設備・監査の4基準から構成されており、合計309項目もの基準が定義されています。 提供する金融サービスの特性に対して遵守すべき項目を精査し、設計・実装していくわけですね。

f:id:iselegant:20200609225948p:plain

なお、第8版では、金融庁の検査マニュアルの検証ポイントとしても定められていました。

参考:金融情報システムと FISC安全対策基準について

※「定められていました」と過去形で述べたのは、現在では実施重視の最低基準の検証や対話を重視する方針に変更となり、金融庁の検査マニュアル自体が廃止されています(金融検査・監督の考え方と進め方 (検査・監督基本方針))。

このFISC安全対策基準遵守が、金融系でシステムを作る際の堅牢さ・大変さの理由の一つと言えるでしょう。

以上より、FISC安全対策基準は日本の金融サービスにおいて、安全性指標のデファクトスタンダードとしての地位を築き、事実上の守るべきルールとして位置付けられています(と、私は理解しています)。

実は十分にお伝えできなかったデータ保護の観点

FISC安全対策基準は本当に色々な観点の基準が規定されています。 著作権の関係上、ここでFISC安全対策の詳細な内容に触れることはできませんが、データ保護に関しても規定されています。 ざっくりで述べると、保存・転送データに対する漏洩防止策やファイルに対するアクセス制御に関する基準が存在します。

※この「ファイル」には、プログラミング言語で書かれたソースコードも含まれています。

先程のCI/CD構成図では、以下の箇所が保護すべきデータ保存場所に該当します。

f:id:iselegant:20200609225953p:plain

登壇内容では、各AWSサービスの暗号化とCodeCommitの権限について触れました。しかし、発表時間の都合上、S3とECRのアクセス制御観点については見送りとしました。本ブログではこのトピックに触れていきます。

データ保護が十分でない場合のリスク

上述の通り、このブログでは、FISC安全対策基準の文脈からデータ保護の観点を述べています。 一方、この文脈に関わらずデータ保護の観点は重要です。 CI/CDにおいてデータ保護が不十分である場合、どのようなリスクが介在しているのでしょうか? 筆者は主に以下の点をリスクとして考えています。

リリース対象プログラムが改ざんできてしまう

本番用プログラムがいつどのような内容で配布されたかを証跡と残すことは運用統制上必要となります。 しかし、アクセス権限が不十分である場合、証跡としての役割も担うリリース対象プログラム自体が改ざん可能となってしまいます。

品質保証外のアプリケーションリリースができてしまう

手動オペレーションにより本番用プログラム修正を介在してしまうことで、品質保証されないアプリケーションをリリースを許容することになってしまいます。これは、CI/CD実現した安定的なリリースフローを破壊しかねません。

稼働中のアプリケーション稼働への影響が発生する

ECRに保存された稼働対象のDockerイメージを誤って削除してしまった場合、本番デプロイが失敗するだけでなく、イメージを取得できずにスケールアウトできない等の不都合が生じます。

遵守すべきガイドラインから逸脱してしまう

金融系システムの場合、FISC安全対策基準に遵守しているとは言えず、金融サービスとしての安全性・信頼性・堅牢性が揺らいでしまいます。

今回扱う構成図では、S3はCI/CDパイプラインの中間ファイル、ECRはアプリケーション稼働のテンプレートイメージとしての位置付けですが、上記の内容から、これらに対するデータ保護が重要である点が理解できるかと思います。

データ保護の実装の考え方

ここまでの内容を考慮すると、目指すべき対応方針としては、S3とECRに然るべき権限のみを付与すればよいことになります。 具体的には、以下のような方針が立つでしょう。

  • S3はCodePipelineからのアクセスを許可し、IAMユーザー等からの手動操作を禁止する
  • ECRはCodeBuildからのアクセスを許可し、IAMユーザー等からの手動操作を禁止する

以降、実際のIAMポリシー内容を基に実装例を確認していきます。

S3のデータ保護

S3ではバケットポリシーを活用して、そのバケットをアクセス対象を制限することができます。

Amazon S3 でのポリシーとアクセス許可 - Amazon Simple Storage Service

たとえ、IAMユーザーやIAMロールにてS3アクセス用のIAMポリシーを付与されていたとしても、バケットポリシー側定義でDenyされていればアクセスすることができません。

今回の例で言えば、CodePipelineがS3内のアーティファクトの保存・取得をAllowし、マネジメントコンソール等を通して操作されるIAMユーザーからの更新権限を明示的にDenyすれば良さそうです。

一方、今回の構成では、CodeCommitからのソースコード受け渡しの際にはクロスアカウント側処理が必要となります。以下のように、作業用アカウントIDからのソースコード受け渡しが必要となりますので、明示的に参照・更新権限をAllowする点には注意が必要ですね。

f:id:iselegant:20200609232040p:plain

※以降に示すIAMポリシー例は一部内容を簡易的に記載した例になります。より良いポリシーの記述方法があるかもしれませんが、その点はご容赦ください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "s3:Put*",
        "s3:Delete*"
      ],
      "Resource": "arn:aws:s3:::[CodePipelineアーティファクトS3バケット名]/*",
      "Condition": {
        "ArnLike": {
          "aws:PrincipalArn": [
            "arn:aws:iam::[プロダクション環境用AWSアカウントID]:root",
            "arn:aws:iam::[プロダクション環境用AWSアカウントID]:role/[クロスアカウント用IAMロール名]"
          ]
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[作業用AWSアカウントID]:root"
      },
        "Action": [
          "s3:Put*",
          "s3:Get*"
        ],
        "Resource": "arn:aws:s3:::[CodePipelineアーティファクトS3バケット名]/*"
      },
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::[作業用AWSアカウントID]:root"
      },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::[CodePipelineアーティファクトS3バケット名]"
    }
  ]
}

ECRのデータ保護

ECRについても、S3と同様にリポジトリポリシーを利用することでアクセス制御が可能です。

Amazon ECR のリポジトリポリシー - Amazon ECR

今回のIAMポリシー例では 、CodeBuild以外のイメージ更新をDenyすることで実現できます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDeleteAndPutPolicy",
      "Effect": "Deny",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "ecr:BatchDeleteImage",
        "ecr:DeleteRepository",
        "ecr:DeleteRepositoryPolicy",
        "ecr:PutImage"
      ],
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalArn": "[CodeBuildにアタッチされたIAMロールのARN]"
        }
      }
    }
  ]
}

上記はプロダクション環境のECRリポジトリに関する記載例です。 もし、ステージング環境のポリシーを設定する場合、プロダクション環境のCodeBuiild上からDockerイメージを取得するための許可が必要なので、Statement内に同列で以下を追加する必要があります。

※IAMポリシーの一部抜粋

    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ],
      "Condition": {
        "ArnLike": {
          "aws:PrincipalArn": "[プロダクション環境のCodeBuildにアタッチされたIAMロールのARN]"
        }
      }
    }

さて、ここで読者の皆さんの中には、「IAMユーザー操作からのECR操作を拒否し、CodeBuildからの操作を許可するのであれば、以下でも良いのでは?」と疑問をお持ちの方もいらっしゃるのではないでしょうか?

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCodeBuildPolicy",
      "Effect": "Allow",
      "Principal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": "*"
    },
    {
      "Sid": "DenyIAMUserOperationPolicy",
      "Effect": "Deny",
      "Principal": {
        "AWS": "arn:aws:iam::[プロダクション環境用AWSアカウントID]:root"
      },
      "Action": [
        "ecr:BatchDeleteImage",
        "ecr:DeleteRepository",
        "ecr:DeleteRepositoryPolicy",
        "ecr:PutImage"
      ]
    }
}

この点について、AWSサポートからの回答によると、現状上記では期待する動作にならない(つまり、IAMユーザーから手動でイメージの追加や削除ができてしまう)ことが確認できています。この点は実装の際にご注意ください。

※動作しない理由については現時点で言及されていません。内容更新あり次第、追記予定です。

また、以下についても同様の理由で動作しないようです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyUpdatePolicyExceptCodeBuild",
      "Effect": "Deny",
      "NotPrincipal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": [
        "ecr:BatchDeleteImages",
        "ecr:DeleteImages",
        "ecr:DeleteRepository",
        "ecr:DeleteRepositoryPolicy",
        "ecr:PutImage"
      ]
    }
  ]
}

開発環境における個別設計の検討

手動ECRアクセスの許可

ここまでの内容では、プロダクション環境を主軸にアクセス制御についてお話ししてきました。 プロダクション環境はステージング環境のECRからのイメージを取得、デプロイを行うことから、プロダクション側稼働品質を保証するために、ステージング環境も同様にアクセス制御を設けることが必要です。

では、開発環境のECRはどうでしょうか? 障害発生時の切り分けやAWSリソースへのアクセスロジックが含まれるアプリケーション開発の一時的な稼働確認などを行うために、わざわざCodeCommit上のCommitログを汚し、CI/CDパイプラインに乗せて稼働確認を行うことは避けたいケースもあります。

このような理由から、開発環境に関してはECRのアクセス制御はある程度緩めてもいいかと筆者は考えています。 一方、このような個別設計を行うと、別の代償が発生することが多々あります。

例外に伴う代償

ECRでは、イメージに関するライフサイクルポリシーを設定できます。これにより、ルールに従って保持するDockerイメージの対象が管理され、コスト削減を行うことができます。

Amazon ECR ライフサイクルポリシー - Amazon ECR

開発者が多数のDockerイメージを手動pushすると、世代が頻繁に更新されます。開発環境のECRイメージはCI/CDにてステージング環境から取得されますが、仮に開発環境ECRに過去数世代分を保持するポリシーを設定した場合、 CI/CD用のイメージが期限切れとなりパイプラインが正しく動作しなくなるリスクが高まります。 そのようなケースでは、保持する世代数を多く設定して未然に防ぐ、手動push時のイメージタグプレフィックスとCI/CDでのpush時イメージタグプレフィックスが衝突しないようにルール化して手動イメージ側のみライフサイクルポリシーを適用させる、ECR自体を分離させる、再度CI/CD実行の運用フォローを行う、といった対応が必要になります。

f:id:iselegant:20200609231941p:plain

また、手動でpushするECRイメージタグが既存のタグと重複してしまうと、CI/CDにより予期せずステージング環境にデプロイされてしまう可能性もあります。 そのため、タグの重複禁止に関する考慮も必要です。

イメージタグの変更可能性 - Amazon ECR

f:id:iselegant:20200609231841p:plain

このように、ある程度個別の設計を行うと、上記のように運用でカバーしたり新たなルールが必要となります。 代償を許容してでも、その個別設計が必要かどうかは組織の運用チーム毎に改めて検討が必要でしょう。

アクセルとブレーキをうまく使い分ける

上述した個別設計ですが、これはある程度開発者に対する自由度を持たせる発想です。 つまり、アジリティを高めるために、ある程度厳密なルールは崩すものの、最低限の防衛ラインを定めるという考え方です。

今回ご紹介した構成では、例え開発環境ECRのタグがライフサイクルポリシーにより消失してしまったとしても、ステージング環境側ではタグを保存する(データを保護する)ことで、最悪プロダクション環境への影響を防ぐことができます。

開発環境で開発アジリティを高め(アクセルを踏み)、ステージング環境で稼働保証を行う(ブレーキを踏む)というバランスを考慮しつつ検討を行いました。

このように、全てブレーキを踏んだ設計を行うのではなく、その中でどこにアクセルを踏んだ設計を行うのか、考慮してみると良いかもしれません。

※安全性の確保ばかりに目を向けると、正直かなり手狭な運用になってしまうと私は考えています。

セキュリティ設定の煩雑化に関する解決策

本稿はセキュリティ対応の一例ですが、その他多数のセキュリティの設定を行っていくと、実装や設定内容の管理が煩雑になります。 また、環境やアプリケーション機能が増えると、その分同じような設定を施さなくてはなりません。

そのため、CloudFormationやTerraformといったIaCツールの活用し、コードレベルで再利用可能な範囲を定めてモノ作りしていくことが重要となります。

例えば、IAMロールやIAMポリシーは各ECRで共通に扱いますが、ECRやCodePipelineはアプリケーション毎に増えていくため、再利用頻度が異なるため、別々の粒度で管理するのが良いでしょう。

実際、再利用可能な単位を見極めてコーディングしていくことはサービスの拡張方針に強く依存しますし、かなりセンスと経験が求められると筆者は考えています。 この点の考察に関しては、別途ご紹介できればと思います。

Well-Architectedの観点から設計要所を把握する

AWSでは、Well-Architectedフレームワークという形で過去のアーキテクチャ実績を基にしたベストプラクティスを提供しています。

Well-Architectedフレームワークでは5つ観点で設計原則が定義されており、その内の一つである「セキュリティ」観点でも今回の設計の必要性に関する問いが設けられています。

SEC 2: ⼈為的なアクセスをどのように制御していますか? 定義されたビジネス要件に合致するコントロールを実装することで⼈為的なアクセスを制御 し、リスクと不正アクセスの影響を軽減します。これは特権ユーザーと AWS アカウントの管理者に適⽤されます。また、アプリケーションのエンドユーザーにも適⽤されます。

参考:Well-Architectedフレームワーク

また、AWSでは金融系サービス向けに個別のWell-Architectedフレームワークも提供しており、そちらは下記例のように厳格な観点での問いが定義されています。

FSISEC1: How do you ensure that AWS IAM Roles are compliant with the principle of least privilege?

FSISEC3: How do you accommodate segregation of duties as part of your IAM role design?

FSISEC6: How do you ensure isolation between SDLC environments (dev, test, prod)?

参考:Financial Services Industry Lens - AWS Well-Architected Framework

これらドキュメントも併せて目を通しておくことでより多角的な視点で検討を行うことができるのでおすすめです。

最後に

今回はデータ保護の視点からみたCI/CDをトピックとして取り上げてみました。 FISC安全対策基準観点からのデータ保護の重要性や、CI/CD構成上で実際に手当が必要なサービスとその設定に触れ、開発アジリティに対するバランス等に関する考察まで述べました。

以下の通り、AWSはセキュリティを最重要項目として捉えています。責任共有モデルのもと、AWSを利用するユーザーも常にその点は意識して取り組まなければなりません。

アマゾン ウェブ サービス (AWS) のお客様にとって最も重要なことは、情報のセ キュリティです。セキュリティは、偶発的または意図的な盗難、漏洩、不整合、 削除からミッションクリティカルな情報を保護する機能要件の中核部分です。

参考:AWSセキュリティのベストプラクティス

CI/CDでセキュリティ設計を検討する際の1つの知見として、今回の記事が読者の皆さんに役立つと嬉しいです。

次回はDevSecOps関連の取り組みについてご紹介できればと思います。

※ちなみに、ここでの内容はすべて筆者の私見であり、所属する組織の意見でも代表するものでもありません。また、あくまで特定の秘匿情報ではなく公開可能な情報から述べているため、その点はご留意ください。

ではまた。