RDSスナップショットを、テスト用にマスクする、CodeBuildとdbtestdataで


本番RDSスナップショットをそのままテスト用に使うわけにいかない。個人情報とか業務上の機密とか。マスクします。みなさんどうやってるんですかね。

全体像

こんな流れで作ります。

  1. create RDS Instance
  2. Data masking
  3. RDS create snapshot
  4. RDS instance shutdown

この記事では 2. のところを扱います。ほかは手作業。そのうちawscliとCodeBuildで自動化する。

マスク設定ファイルをつくるのに必要な情報を用意する

  • information_schema.tables, columnsを漁る
  • テーブルそのものの要否をふりわける
  • 必要なテーブルについて、マスクすべきカラムを選別する
  • カラムごとに、どんなデータパターンでマスクするか決める

マスク設定ファイルをつくる

マスクツールは dbtestdata を使います。dbtestdata用のマスク設定ファイルを作ります。dbtestdataがMySQLしか対応してないので、MySQLかAurora(MySQL互換)にしか使えないのがツラい。

DBマイグレーションを管理しているgitリポジトリに、 dbmask/dbtestdata.update.${TABLE_NAME}.conf と作っていくことにします。テーブルごとに1ファイル作ります。このように。

package sample;

use strict;
use warnings;
use data::VariableDataGenerator;
use sql::VariableSQLGenerator;
use utf8;

return {
    name => __PACKAGE__,
    update => {
        users => {
            primary => "id",
            clazz => {
                name => RANDOM_JA_NAME_KAN,
                email => RANDOM_EMAIL,
                address => RANDOM_JA_PREF,
                tel => RANDOM_JA_TEL,
            }
        }
    }
};

カラムごとに、どのようにマスクするかをチクチク書いていきます。マスク不要なカラムは記載不要です。なにがどうなってるのかは、 GitHub - dino-tools/dbtestdata: database testdata generator を漁ってください。

DBマイグレのgitリポジトリに入れておきたいのは、DBマイグレによるスキーマ変更と同調して、マスク設定もアップデートしていきたいからです。テーブルごとにしたいのは、gitでコンフリクトを少なくしたいのと、CodeBuildで並列実行させたいからです。

RDSインスタンスを立てる

RDSスナップショットから、RDSインスタンスを立てます。本番環境とは別のVPCにするのが事故防止のためにも良いでしょう。CodeBuildから接続できるよう、RDSインスタンス側のセキュリティグループを調整しておきます。

Auroraなら、たとえばこのように。

CodeBuildビルドプロジェクトをつくる

buildspec.ymlはこんな。 envの部分は、よしなに置き換えて使ってください。RDSに接続するので、このCodeBuildはVPC内で動かします。Dockerイメージは、惰性で python:3.8-buster を利用してます。wgetとか要るのか、、、なんか要らない気がしてきた。

version: 0.2

env:
  variables:
    RDS_ENDPOINT: "hoge"
    RDS_USER: "hoge"
    CONF_FILE: "hoge"
    PERL5LIB: "."
  parameter-store:
    RDS_PASSWD: /CodeBuild/RDS_PASSWD

phases:
  install:
    commands:
      - env | sort
      - echo "dash dash/sh boolean false" | debconf-set-selections
      - DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash
      - apt update
      - apt install -y wget unzip jq mariadb-client libdbi-perl libdbd-mariadb-perl
      - git clone https://github.com/dino-tools/dbtestdata.git /usr/local/dbtestdata
  build:
    commands:
      - |
        cd /usr/local/dbtestdata
        echo ${RDS_PASSWD} \
        | perl dbtestdata.pl update \
            --hostname=${RDS_ENDPOINT} \
            --database=production_asp \
            --username=${RDS_USER} \
            --password \
            --conf=${CODEBUILD_SRC_DIR}/dbmask/${CONF_FILE}

ちなみにCodeBuildは実行時間が8時間までの制限があるので、クソデカいテーブルには制限時間オーバーで駄目になります。ラッキーなことに、8時間以内に終わるテーブルだけだったので、今後そのうち考えます。たぶんFargateとかでやると思います。いやどうだろう、わからない。

マスクする

こちらもCodeBuildにやらせます。テーブル数が多い場合は、同時実行数を絞ったり、RDBのグレードを上げたりの工夫が必要になるかと思います。両方で同じバージョンを利用するよう、 --source-version "${CODEBUILD_SOURCE_VERSION}" を付けてます。

phases:
  install:
    commands:
      - env | sort
      - echo "dash dash/sh boolean false" | debconf-set-selections
      - DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash
      - apt update
      - apt install -y wget unzip jq
      - pip install --upgrade --quiet pip
      - pip install --quiet awscli
      - aws --version
  build:
    commands:
      - |
        cd ${CODEBUILD_SRC_DIR}/dbmask;
        ls dbtestdata.update.*.conf \
        | sort \
        | while read CONF_FILE; do
            aws codebuild start-build \
                --source-version "${CODEBUILD_SOURCE_VERSION}" \
                --project-name dbtestdata-runner \
                --environment-variables-override name="CONF_FILE",value="${CONF_FILE}",type="PLAINTEXT"
        done

要らんテーブルは空にする

テストの要件などから、空でいいテーブルが多々あります。dbtestdataにもdeleteする機能はありますが使いません。外部キー制約の on delete が動いてしまったりで厄介だったので、mysqlコマンドでtruncateすることにしました。 --init-command="SET SESSION FOREIGN_KEY_CHECKS=0;" を付けたかった。 ${CODEBUILD_SRC_DIR}/dbmask/delete.conf は、1行1テーブル名の、ただのテキストファイルです。

version: 0.2

phases:
  install:
    commands:
      - env | sort
      - echo "dash dash/sh boolean false" | debconf-set-selections
      - DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash
      - apt update
      - apt install -y wget unzip jq mariadb-client
  build:
    commands:
      - |
        cat ${CODEBUILD_SRC_DIR}/dbmask/dbtestdata.delete.conf \
        | sort -u \
        | while read TABLE_NAME; do
            mysql -u ${RDS_USER} -p${RDS_PASS} -h ${RDS_HOST} production_asp \
                --init-command="SET SESSION FOREIGN_KEY_CHECKS=0;" \
                -e "truncate table ${TABLE_NAME};"
        done

RDSスナップショットを作って、開発アカウントに共有し、RDSを立てる

他のアカウントに共有するにはこのように。

aws rds modify-db-snapshot-attribute \ 
    --db-snapshot-identifier masked-snapshot \ 
    --attribute-name restore \ 
    --values-to-add '["xxxxxxxxxxxxxxxxx"]'

DBマイグレーションに並走する

ほっとくとマスク定義が古くなっていきます。テーブルの追加削除もあります。カラムの追加削除もあります。アプリケーションチームと、DBマイグレの都度、マスク定義ファイルのアップデートもしていくよう、ルール作りを忘れずに。