IAM 条件コンテキストキーで lambda:SourceFunctionArn が使えるようになったので、バージョン・エイリアス別に権限の分離が出来るようになったのかを検証してみた

2022.07.24

いわさです。

先日のアップデートで、条件コンテキストキーで Lambda 固有のlambda:SourceFunctionArnが使えるようになりました。
実は Lambda 周りの条件コンテキストキーを使って、ちょっとしたセキュリティ制御を行いたかったのですが機能が足りずに挫折したことがあります。
今回のアップデートでどこまで制御出来るようになったのか検証してみました。

何が出来るようになったのか

こちらを使うことで従来の条件に加えて、対象関数を条件キーに加えることが出来るようになります。
コンテキストキーにはグローバルに定義されているものと、リソース毎に個別に定義されているものがあって、以下で確認することが出来ます。

lambda:FunctionArnがもともと使えるんじゃないか?という気もするのですが、こちらはLambdaを呼び出すためのイベントソースマッピングを対象に使用するもので、カレントの関数に何かをさせるために使うものではありませんでした。
よって、これまでは Lambda にアタッチする実行ロール次第で何が出来るかを制御する必要がありました。

今回のアップデートで新たにlambda:SourceFunctionArnが導入され、これを使うことで実行元の関数を制御することが出来るようになりました。

同じ実行ロールを別々の関数に付与してみる

ここでは Lambda 関数から Secret Manager へアクセスするシンプルな関数を例に挙げてみます。
同一のシークレットへアクセスする同一実装内容、同一実行ロールの Lambda 関数を2つ用意します。

lambda_function.py

import json
import boto3

def lambda_handler(event, context):
    
    secret_name = "hoge0724prod"
    region_name = "ap-northeast-1"

    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    get_secret_value_response = client.get_secret_value(
        SecretId=secret_name
    )
    secret = get_secret_value_response['SecretString']

    return {
        'statusCode': 200,
        'body': secret
    }

以下は、シークレットへのアクセスを許可するために実行ロールへ追加したインラインポリシーです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:hoge0724prod-vzV5sM",
            "Condition": {
                "ArnLike": {
                    "lambda:SourceFunctionArn": "arn:aws:lambda:ap-northeast-1:123456789012:function:hoge0724secretaccess"
                }
            }
        }
    ]
}

なぜかArnLikeを使ってしまったのですが、よく考えたらArnEqualsで良かったかもしれない。

それぞれの関数に実行ロールをアタッチして実行してみましょう。

lambda:SourceFunctionArnで許可した関数でだけ、シークレットにアクセスが出来ましたね。
特定の関数以外は指定したシークレットにアクセスできなくするなど、追加のセキュリティレイヤーのような形で使うことが出来るようになりました。

条件に指定する ARN

さて、冒頭で以前挫折した旨をお話しましたが、その内容を確認してみたいと思います。

先程のようにシークレットにアクセスする Lambda 関数を用意したときに、Production(Prod) 用と Development(Dev) 用にシークレットが分かれていたとします。
本番シークレットには、Prod用のエイリアスからのみアクセスを許可したかったのです。当時はアクセス元の関数情報で細かい制御を行うことが出来なかったのでタグやら何やら色々試したのですが挫折しました。
その時は結果としては、バージョン作成前に実行ロールを本番用に切り替えるという方法が良いかなと落ち着きました。

Lambda はエイリアスやバージョンが関数 ARN に含まれるのですよね。
ですので、今回のアップデートで良い感じに出来るのではないかなと。

エイリアスを指定してみる

エイリアスを作成し、以下のようにlambda:SourceFunctionArnを対象エイリアスのARNに設定してみましょう。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:hoge0724prod-vzV5sM",
            "Condition": {
                "ArnLike": {
                    "lambda:SourceFunctionArn": "arn:aws:lambda:ap-northeast-1:123456789012:function:hoge0724secretaccess:alias-prod"
                }
            }
        }
    ]
}

実行してみたところ、alias-prodでもalias-devでも、開発中の$LATESTでも全てで失敗するようになりました。
逆に、変更前のポリシーではエイリアスの場合でもシークレットへアクセスが出来ました。

バージョンを指定してみる

コンテキストにエイリアス情報は含まれていなさそうです。
では、バージョンではどうでしょうか。試してみましょう。

まずはポリシーにバージョンを指定しない状態で、バージョンからテスト実行を行ってみます。

成功しました。
こちらもバージョン情報はコンテキストには含まれていないようですね。

念の為、lambda:SourceFunctionArnにバージョンを指定してみます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:hoge0724prod-vzV5sM",
            "Condition": {
                "ArnLike": {
                    "lambda:SourceFunctionArn": "arn:aws:lambda:ap-northeast-1:123456789012:function:hoge0724secretaccess:1"
                }
            }
        }
    ]
}

こちらはやはり、失敗するようになりました。

さいごに

本日は、IAM 条件コンテキストキーで lambda:SourceFunctionArn が使えるようになったので、バージョン・エイリアス別に権限の分離が出来るようになったのかを確認してみました。

まとめると以下のような感じでした。

  • 同一ポリシーでコンテキストの関数ARNごとにセキュリティの分離が出来るようになった
  • バージョン・エイリアスについてはコンテキストに含まれていなさそうなのでそのあたりを理解して使う