おーい磯野、インドネシアの個人情報保護法の対応しようぜ

に公開

はじめに

最近サザエさん見てないな、と少し寂しく感じながらブログを書きました。
中島くんがこのセリフを言っている回探そうかなと思ったんですが
Yahoo知恵袋にこんな回答が

これまじ?

というわけで今回はインドネシアの個人情報保護法に対応したお話をします。

インドネシアの個人情報保護法とは

簡単にインドネシアの個人情報保護法について触れます。
インドネシアでは個人情報を扱う事業者に対して様々な規制が課されています。
中でもデータの扱いに関連する要件として:

  • 個人情報データはインドネシア国内にデータソース(コピーでも可)を置く必要がある
    が挙げられました。
    今回はこちらにどう対応したかを書いていきます。

対応方針の検討

方針を決める上で考慮した観点がこちら

  • バックアップデータ量とそれに伴う保存コスト
  • 管理コスト(設定変更の頻度や複雑さ)
  • 変更コスト(特に将来的なマルチリージョン構成への対応可能性)
  • インドネシアにデータを置くセキュリティリスク

続いて検討した方法がこちら

  • AWS Backupを利用した自動バックアップ
    • マネージドのバックアップサービスで、対象リソースを選んで簡単にS3へのバックアップが可能
    • リソースの中のどのデータをバックアップ対象にするか選べないのがネック
  • RDSのS3エクスポート機能でエクスポートしたものをクロスリージョンレプリケーション
    • RDSからエクスポートしたS3(東京リージョン)→ S3(ジャカルタリージョン)へのクロスリージョンレプリケーションの設定をする
    • エクスポートするテーブルを選択できるので、データのリスクは抑えられる
  • RDSのS3エクスポート機能でエクスポートしたものを、Glueで整形してジャカルタリージョンのS3に置く
    • S3に上がったデータをGlueで整形してインドネシアの個人情報データだけS3に置く処理をさせる
    • 保存コストとデータリスクを抑えられるが、Glueの運用コストが気になる
  • マルチリージョン構成によるデータの完全分離
    • 利用ユーザーのリージョンごとに完全にデータソースを分離する方法
    • 理想ではあるが、変更コストが高い

表にまとめるとこんな感じ

対応方法 保存コスト 管理コスト 変更コスト データリスク
AWS Backup
S3クロスリージョンレプリケーション
Glueによるデータ整形
マルチリージョン対応

決定した方針

AuroraのS3エクスポート機能で東京リージョンのS3にエクスポートし、そのバケットをクロスリージョンコピーする方式を採用しました。
採用理由は

  • 目的を個人情報保護法対応に絞ることができる
    • マルチリージョン構成にすると目的が広がりすぎてしまう
  • 不要なデータを他リージョンに置くリスクを小さくできる
  • コスト効率が良い

この3つの点が大きかったです。

CDKによる実装例

この構成をCDKで書いたのがこちら。

実装は大きく2つのスタックで構成されています:

  1. ジャカルタリージョンのS3バケット構築(JakartaS3Stack)
// ジャカルタリージョンにS3バケットとKMSキーを作成
export class JakartaS3Stack extends Stack {
  public readonly destinationBucket: Bucket;
  public readonly destinationKmsKeyIdSsmParameterName: string;

  constructor(scope: Construct, id: string, props: Props) {
    super(scope, id, props);

    // KMSキーの作成
    const destinationKmsKey = new Key(this, 'DestinationBucketKey', {
      alias: 'indonesia-personal-data-destination',
      enableKeyRotation: true,
    });

    // ジャカルタリージョンのS3バケット
    this.destinationBucket = new Bucket(this, 'DestinationBucket', {
      bucketName: props.destinationBucketName,
      versioned: true,
      encryption: BucketEncryption.KMS,
      encryptionKey: destinationKmsKey,
      removalPolicy: RemovalPolicy.RETAIN,
      lifecycleRules: [
        {
          id: 'MoveToDeepArchiveAfterOneDay',
          transitions: [
            {
              storageClass: StorageClass.DEEP_ARCHIVE,
              transitionAfter: Duration.days(1),
            },
          ],
        },
      ],
    });

    // KMSキーIDをSSMパラメータに保存(クロスリージョンアクセス用)
    const stack = Stack.of(this);
    const parameterName = `${stack.stackName}.DestinationBucketKeyId`;
    new StringParameter(this, 'DestinationSSMParam', {
      parameterName,
      description: 'The KMS Key Id for the destination s3 bucket stack',
      stringValue: destinationKmsKey.keyId,
    });
    this.destinationKmsKeyIdSsmParameterName = parameterName;
  }
}
  1. 東京リージョンでのデータエクスポート設定(IndonesiaPersonalDataExportStack)
export class IndonesiaPersonalDataExportStack extends Stack {
  constructor(scope: Construct, id: string, props: Props) {
    super(scope, id, props);
    
    // 東京リージョンにS3バケットを作成
    this.sourceBucket = new Bucket(this, 'SourceBucket', {
      bucketName: sourceBucketName,
      encryption: BucketEncryption.S3_MANAGED,
      versioned: true,
      removalPolicy: RemovalPolicy.RETAIN,
      lifecycleRules: [
        {
          id: 'MoveToDeepArchiveAfterThreeDays',
          transitions: [
            {
              storageClass: StorageClass.DEEP_ARCHIVE,
              transitionAfter: Duration.days(3),
            },
          ],
        },
      ],
    });
    
    // RDSからS3へのエクスポート設定(ここではLambdaがSDKを実行してエクスポートタスクを作っています)
    const exportDbSnapshotToS3Lambda = new LambdaEventBridge(
      this,
      'ExportDbSnapshotToS3',
      {
        // ...設定省略
        rule: stage === 'cron(0 0 * * ? *)', 
        envVars: {
          SERVICE_NAME: serviceName,
          CLUSTER_IDENTIFIER: dbClusterIdentifier,
          SNAPSHOT_TYPE: 'automated',
          EXPORT_S3_BUCKET: this.sourceBucket.bucketName,
          IAM_ROLE: exportRole.roleArn,
          KMS_KEY_ID: this.kmsKey.keyId,
        },
      }
    );
    
    // クロスリージョンレプリケーション設定(L2はまだないっぽいのでL1で実装)
    cfnBucket.replicationConfiguration = {
      role: this.replicationRole.roleArn,
      rules: [
        {
          destination: {
            bucket: props.destinationBucket.bucketArn,
            encryptionConfiguration: {
              replicaKmsKeyId: stack.formatArn({/*...省略*/}),
            },
          },
          status: 'Enabled',
        },
      ],
    };
  }
}

実装のポイント

設定で意識したポイントはこちら

  • コスト最適化:S3ライフサイクルルールによるDeep Archiveへの早期移行
    • 1日単位でバックアップは更新されていくので古いデータはどんどんDeep Archive
  • 自動化:EventBridgeによる定期的なRDSのS3エクスポート自動化
    • RDSのS3エクスポートタスクをEventBridge+LambdaからSDKを叩くようにすることで自動化
  • クロスリージョン連携:SSMパラメータストアを使用してKMSキーIDを共有
    • 東京リージョンからジャカルタリージョンのS3にレプリケートするためにKMSキーを共有する必要があり、今回はパラメータストアに保存して共有する形に。

まとめ

今の会社に入って、初めてインドネシアでのサービス展開とAWSのジャカルタリージョンの利用をしました。
大きな会社のエンジニアさんが書かれているブログや登壇内容などで
クロスリージョン設定についてはキャッチアップはしていましたが
サービス立ち上げ初期から実際に自分が手を動かしてクロスリージョン設定をすることになるとは思ってませんでした。

今回は比較的実装コストなどを抑えた構成を取ることになりましたが
将来的にマルチリージョン構成も構想に入っているのでチャレンジしていきたいと思っています!

DRESS CODE TECH BLOG

Discussion