暗号化に AWS Key Management Service(kms)を利用して、バックエンドを kotlin を使っている場合に開発環境と商用環境両方を考慮した実装方法がなかなか見つからなかったのでこうやったって話を記載します。
前提
- 開発環境は アプリ側はtomcatで起動、kmsはlocalstackを利用している
- 商用環境は アプリ側はeksでpodを起動、AWS kmsを利用してterraformでprincipalsでpodのiamロールでアクセスを許可
実現方法
ポイントはKmsClientの生成方法。
localstackを利用している開発環境はKmsClientをbuildするときに、endpointを指定することでVPCを経由することになりモックで強制的にハックすることができる。
利用したライブラリー
ちょっとハマったので利用するライブラリーも記載。
kotlin("jvm") version "1.9.0"
kotlin("plugin.spring") version "1.9.0"
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")
implementation("aws.sdk.kotlin:kms:0.21.5-beta")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.7.3")
localstackは1.4を利用。
実装
1 . application.yaml はこんな感じ
aws:
region: "${AWS_REGION}"
kms:
endpoint: "${AWS_KMS_ENDPOINT:}"
keyId: "${AWS_KMS_MASTER_KEY_ID:}"
2 . 環境変数を変える
- 開発環境は以下のように設定する
AWS_REGION=ap-northeast-1
AWS_KMS_MASTER_KEY_ID=alias/hodeservice/hoge
AWS_KMS_ENDPOINT=http://localhost:4566
- 商用環境は以下のように設定する
AWS_REGION=ap-northeast-1
AWS_KMS_MASTER_KEY_ID=alias/hodeservice/hoge
AWS_KMS_ENDPOINTを指定しない
3 . KmsClientの生成を工夫する
private val kmsClient: KmsClient = KmsClient {
region = awsConfig.region
endpointUrl = awsConfig.kms.endpoint?.takeIf { it.isNotEmpty() }?.let { Url.parse(it) }
}
suspend fun decryptCiphertextKeyToPlainTextKey(ciphertextKey: ByteArray): ByteArray {
val decryptKeyRequest = DecryptRequest {
this.ciphertextBlob = ciphertextKey
this.keyId = awsConfig.kms.keyId
}
return kmsClient.decrypt(decryptKeyRequest).plaintext ?: throw FailedToDecryptDataKey()
}
こんな感じでAWS_KMS_ENDPOINTの設定有無で生成するKmsClientの方法を変えることで、EndpointProviderが適切になり両環境で上手く動作するようになる。