原文はこちら
最近このブログではOracle Functionsに関連する様々なテーマについてご紹介してきましたが、今回ご紹介するのはもしかするとこのブログシリーズで最初に取り上げるべきだったかもしれないテーマです。以前のポストでアプリケーションとファンクションの設定変数の設定方法をご説明したんですが、こうした設定変数をセキュアに保持しておく方法はそのときにはご説明していませんでした。このポストでは、Oracle CloudテナンシーのKey Managementを使って設定を暗号化、復号することによってこのセキュアな保持を実現する方法をご説明します。
これをやるにはいくつかのステップを進んでいくことになるので、ここでアウトラインを示しておきますね:
- KMS Vaultの作成
- Master Encryption Keyの作成
- Data Encryption Key (DEK) をMaster Encryption Keyから生成
- DEK
plaintext
から返却された値を使ってsensitive value
を暗号化(オフライン) - 暗号化された
sensitive value
をサーバレスアプリケーションの設定変数として保存 -
sensitive value
の暗号化に使ったDEKciphertext
とinitVector
をファンクションの設定変数として保存 - ファンクションの中でOCIDとCryptographic Endpointを使ってOCI KMS SDKを呼び出し、DEK
ciphertext
を復号してplaintext
に戻す - 復号したDEK
plaintext
とinitVector
を使ってsensitive value
を復号する
ここで
sensitive value
と記載しているものは、暗号化が必要なものであればなんでも有り得ます。データベースパスワードだったり、APIキーだったり、などなど。Oracle Functionsの設定変数の中に平文ではなく暗号化して保存したいものを示すプレースホルダーであり、実態がなんであれ構いません。なんかステップ多いな、って感じですよね。でも実のところ書いてあるとおり進めていけばそんなに大変じゃありません。それに、本当のところ、アプリケーションとファンクションにとってセキュリティというのは一番大事なことですからね。
始める前に
ここではOracle Functionsの中でリソースプリンシパルを活用することになります。これはすなわち、OCI SDKを使う際にOCI設定ファイルを含める必要は生じないということですが、一方で、KMS APIとやり取りするうえでの適切な許可ポリシーを備えたダイナミックグループを作成しておく必要があるということになります。
まず、'Dynamic Group'を作成して'rules'を以下のようにセットしてください(ファンクションをデプロイするコンパートメントのOCIDを使ってください):
ダイナミックグループとリソースプリンシパルをOracle Functionsと組み合わせて使う方法について詳しく知りたければドキュメントを参照ください。次のステップはダイナミックグループ用にポリシーを作り、
keys
, vaults
と key-delegate
の管理権限を与えることです。こちらについても、ポリシーのドキュメントを読んでどのようなポリシーを適用としているのか理解してください。アプリケーションとファンクションの作成
では、サーバレスアプリケーションを作成しましょう(適切なサブネットOCISで置き換えてください):
fn create app --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.phx..."]' fn-kms
そしてファンクションを作成します:
fn init --runtime java fn-kms-demo
依存対象
KMS SDKのための依存関係を
pom.xml
に追加しておく必要があります:Java 11をお使いの場合は、
javax.activation-api
もマニュアルで追記しておきます:Dockerfile
いくつかの環境変数に依存することになるので、お手製の
Dockerfile
を作成する必要があります。デフォルトで実行される Dockerfile
との違いはファンクションをデプロイする際のDockerビルドでのテスト実行をスキップするところだけです。なぜこれをスキップするかというと、現状ではでファンクションをデプロイする際に環境変数をDockerビルドコンテキストに渡す術がないからです。以下のお手本を使ってください:ファンクションをデプロイする前にマニュアルでテストを実行することをお忘れなく!
VaultとMaster Encryption Keyの作成
OCIコンソールのサイドバーから、'Governance and Administration'の配下の'Security'→'Key Management'を選択してください:
'Create Vault'をクリックしてVaultの詳細を入力します:
Vaultが作成できたら、Vaultの名前をクリックするとVaultの詳細が表示されます。詳細画面で'Create Key'をクリックして新しいMaster Encryption Keyを作成し、ダイアログの入力欄を埋めていきます:
Master Encryption KeyのOCIDとVaultの'Cryptographic Endpoint'をコピーしておいてください。これはあとでDBパスワード用のData Encrption Key(DEK)を作成する際に使います。
Data Encryption Key (DEK)の作成
以下のようにData Encrption Key(DEK)をOCI CLIから作成します:
generate-data-encryption-key
から返ってくる ciphertext
と plaintext
の値を手元に持っておいてください。あとですぐに必要になります。DEK
ciphertext
の例I...[random chars]...AAAAAA==
ciphertextをアプリケーションの設定変数として保持させます:
fn config app fn-kms DEK_CIPHERTEXT I...[random chars]...AAAAAA==
DEK
plaintext
の例0…[random chars]...=
パスワードの暗号化
このステップではパスワードをファンクションの中ではなく、オフラインで暗号化します。あとでファンクションは稼働時に復号を行うことになります。スタンドアロンのJavaプログラムの中で先程のDEKを使ってパスワードを暗号化します。以下のお手本を使ってもらってもよいです。
注意:Plug in your DEK
plaintext
の値をプラグインして initVector
にランダムな16byteのStringを与えてください。 initVector
はあとで復号の際に使えるように設定変数として保持します。ランダムな16 byteの
initVector
Stringをアプリケーションの設定変数として保持させます:fn config app fn-kms INIT_VECTOR_STRING [Random 16 byte string]
上述のプログラムの出力した値をコピーしてください。これが暗号化されたパスワードです。これもアプリケーションの設定変数として保持させます:
fn config app fn-kms ENCRYPTED_PASSWORD N...==
最後に、Master Encryption KeyのOCIDとCryptographic Endpointをアプリケーションの設定変数として格納します:
fn config app fn-kms KEY_OCID ocid1.key.oc1.phx...
fn config app fn-kms ENDPOINT https://...-crypto.kms.us-phoenix-1.oraclecloud.com
サーバレスファンクション
これでサーバレスファンクションに暗号化されたパスワードを復号するよう修正を加えることができるようになりました。以下のようになります:
テスト
前述のように、ファンクションのテストをマニュアルで行う必要があります。
ローカルでファンクションをテストできるようにするには、いくつかの環境変数をセットしておく必要があります。GitHubプロジェクトのルートにある
env.sh
を見るとどの変数をセットする必要があるかわかります(あるいは↓からコピーしてください)。これらの値は全て上述のステップから取得できます(格納しておいたアプリケーションの設定変数と対応していることに留意ください)。必要な環境変数をセットしたら、ユニットテストを書きましょう:
デプロイ
以下でファンクションをデプロイします:
fn deploy --app fn-kms
呼び出しは以下:
fn invoke fn-kms fn-kms-demo
復号されたパスワードが返却されるでしょう:
{"decryptedPassword":"hunter2"}
まとめ
このポストではOCI KMSを使ってVaultとMaster Encryption Keyを作成しました。そしてそのMaster Encryption Keyを使ってData Encrption Key(DEK)を作成し、DEKを使って機密情報を暗号化し、その暗号化された機密情報をサーバレスファンクションの設定変数に保存しました。そしてこの暗号化された機密情報にデプロイしたファンクションからアクセスし、ファンクションで使えるように復号しました。
参考情報
Oracle Functions関連のわたしの他のポストもチェックしてみてください:
- Getting Started
- Connecting A Serverless Function to Autonomous DB with Java
- Connecting A Serverless Function to Autonomous DB with Node.JS
- Invoking A Serverless Function with OCI Java SDK
- An Easier Way To Work With Autonomous DB
- Invoking Functions With Cloud Events
ご参考までに、デモの中で使ったコードはGitHubで利用可能です。https://github.com/recursivecodes/fn-kms-demo