ISID テックブログ

ISIDが運営する技術ブログ

CDK で ElastiCache for Redis の RBAC を実装する方法

こんにちは。X(クロス)イノベーション本部 ソフトウェアデザインセンター セキュリティグループの耿です。

ElastiCache for Redis クラスターの認証・認可方式のうち、ロールベースのアクセスコントロール(RBAC)AWS CDK で実装する方法に関する日本語の記事が見当たらなかったため、書き残しておきます。

ElastiCache for Redis の認証・認可方式

ElastiCache for Redis クラスターに対する認証・認可には以下の方法があります。この記事では RBAC を対象とします。

  • 認証なし
    • Redis クラスターと通信さえできれば全てのコマンドを実行可能
  • Redis AUTH
    • 単一のトークン(パスワード)による認証
    • 転送中の暗号化 (TLS) の有効化が必須
    • 認証が成功すれば、Redis クラスターの全コマンドを実行可能
  • RBAC(ロールベースのアクセスコントロール)
    • Redis v6.0から導入された ACL を利用した認証・認可方式
    • ユーザー名とパスワードによる認証
    • 複数のユーザーを作成可能
    • それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能
  • IAM
    • Redis v7.0 から利用可能な認証・認可方式
    • 有効期間が短い IAM 認証トークンを利用
    • 複数のユーザーを作成可能
    • それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能
    • IAM ポリシーにより、どの Redis ユーザーを利用できるか制御可能

リソース構成

CDK による実装を見る前に、リソース構成の概観を整理しておきます。

リソース構成

  • 1つの Redis クラスターには複数の Redis ユーザーグループを追加可能
  • 1つの Redis ユーザーグループには複数の Redis ユーザーを所属可能
  • ユーザーはユーザー名とユーザーIDを持ち、複数のパスワードを設定可能
  • ユーザーに設定するアクセス文字列で、Redis クラスターにおける実施可能なコマンドとアクセス可能なキャッシュキーが制限される
  • Redis ユーザーのパスワードを IaC へ記載するのを回避するため、Secrets Manager を利用
  • アプリケーションの権限に応じて Secrets Manager シークレットへのアクセスを許可し、アプリケーション側の Redis クライアントで Redis ユーザー名とパスワードを利用する

CDK による実装のサンプル

こちら を参考にしました。
以下の例はシンプルに、1ユーザーグループ/1ユーザーの構成にしています。

ユーザーパスワード

Secrets Managerでユーザーパスワードを生成します。default という名前のユーザーは必須で作成する必要があり、今回はこの default ユーザーをそのまま利用することにします。以下はデフォルトの暗号鍵を利用しています。鍵の管理が必要な場合は別途 KMS キーを作成してください。

const userName = "default";
const rbacUserSecret = new secretsmanager.Secret(this, "RedisRbacUserSecret", {
    secretName: "RedisRbacUserSecret",
    generateSecretString: {
        secretStringTemplate: JSON.stringify({ username: userName }),
        generateStringKey: "password",
        excludeCharacters: "@%*()_+=`~{}|[]\\:\";'?,./",
    },
});

ユーザーとユーザーグループ

執筆時点では CDK には ElastiCache の L2 コンストラクトが用意されていないため、L1 コンストラクトで作成します。
アクセス文字列 (accessString) でユーザーの権限を制限できますが、今回は全コマンドと全キャッシュキーへのアクセスを許可しています。

const userId = "user-1";
const rbacUser = new elasticache.CfnUser(this, "RedisRbacUser", {
    userId: userId,
    userName: userName,
    engine: "redis",
    accessString: "on ~* +@all",
    passwords: [rbacUserSecret.secretValueFromJson("password").unsafeUnwrap()],
});
const rbacUserGroup = new elasticache.CfnUserGroup(this, "RedisRbacUserGroup", {
    userGroupId: "user-group",
    engine: "redis",
    userIds: [rbacUser.userId],
});
rbacUserGroup.node.addDependency(rbacUser);

CfnUser に渡すために、Secrets Manager シークレットで生成したパスワードに対して unsafeUnwrap() を呼んでいます。平文のパスワードは Redis ユーザーの作成に使われ、閲覧可能な場所には出現しないため安全な使い方になります。

VPC

Redis クラスターを配置する VPC とセキュリティグループを作成します。

const subnetName = "Isolated";
const vpc = new ec2.Vpc(this, "Vpc", {
    subnetConfiguration: [
        {
            cidrMask: 24,
            name: subnetName,
            subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        }
    ]
});
const redisSG = new ec2.SecurityGroup(this, 'RedisSG', {
    vpc,
    allowAllOutbound: false,
});

Redis クラスタ

Redis クラスターを作成し、先に作成したユーザーグループを関連付けます。

const cluster = new elasticache.CfnReplicationGroup(this, "RedisReplicationGroup", {
    replicationGroupDescription: "my-replication-group",
    engine: "redis",
    engineVersion: "7.0",
    cacheNodeType: "cache.t3.micro",
    cacheSubnetGroupName: new elasticache.CfnSubnetGroup(this, "SubnetGroup", {
        description: "my-subnet-group",
        subnetIds: vpc.selectSubnets({ subnetGroupName: subnetName }).subnetIds,
    }).ref,
    cacheParameterGroupName: new elasticache.CfnParameterGroup(this, "ParameterGroup", {
        description: "my-parameter-group",
        cacheParameterGroupFamily: "redis7",
    }).ref,
    numNodeGroups: 1,
    replicasPerNodeGroup: 1,
    securityGroupIds: [redisSG.securityGroupId],
    atRestEncryptionEnabled: true,
    transitEncryptionEnabled: true,
    userGroupIds: [rbacUserGroup.userGroupId],
});
cluster.node.addDependency(rbacUserGroup);

Secrets Manager シークレットへのアクセス許可

あとは Redis クラスターへアクセスするアプリケーションが Secrets Manager シークレットにアクセスできるように、読み取り権限を付与すれば良いです。(CDKコードは省略します)

CDK による Redis の RBAC の実装例はあまり見かけませんが、簡単に実装できました。
お読みいただいてありがとうございました。


私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。

セキュリティエンジニア(セキュリティ設計)

執筆:@kou.kinyo、レビュー:@nakamura.toshihiro
Shodoで執筆されました