【AWS】iam:PassRoleとsts:AssumeRoleの違い
AWSでロールを扱う際、iam:PassRole
と sts:AssumeRole
の両方が説明に出てくることが多い。どちらもロールを使えるようにするために必要なアクションだが、いまいち違いがわからなかったので改めて調べてみた。
iam:PassRole
まず iam:PassRole
は、ユーザに対して付与するアクセス許可ポリシーで指定できるアクションである。
例えばAWS Batchを使うとき、ジョブの内容に応じたアクセス許可ポリシーをロールに付与しておき、そのロールをAWS Batchで指定しておく必要がある。ジョブが作成されると、AWS Batchがロールを使ってジョブの内容を実行する。
このとき、もしユーザ自身が aws batch submit-job
コマンド等でジョブを登録する場合、 iam:PassRole
アクション内で当該ロールをResourceに指定したポリシーを作り、それをユーザにアタッチする必要がある。これを行わないと、ロールをサービスに渡すことができない。
AWS Batchの公式ドキュメントにある例では、iam:PassRole
アクションはアクセス許可ポリシー内で以下のように記述されている。
{ "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": [ "arn:aws:iam::<aws_account_id>:role/TestRole" ], "Condition": { "StringEquals": { "iam:PassedToService": [ "batch.amazonaws.com" ] } } }
このポリシーをアタッチされたユーザは、 Resource
で指定されたロールをアタッチできるようになる。加えて、 IAM条件キー iam:PassedToService
を使うことで、アタッチの対象にできるサービスがAWS Batchのみに制限されている。
(補足だが、ロールをアタッチできる対象はAWSサービスのみではない。他のAWSアカウントのユーザにロールを付与して、特定のアクションをクロスアカウントで行ってもらうという使い方もある)
個人開発をしている場合、 iam:PassRole
なんて指定した覚えがないという人も多いかもしれない。個人用環境でAWSを触るときによく使われる AdministratorAccess
ポリシーでは、全てのアクションが全てのリソースに対して許可されているので、 iam:PassRole
を意識する必要はなくなっている。また、各サービスごとにあらかじめ用意されているアクセス許可ポリシーを使っている場合も、既に iam:PassRole
が許可されていることが多い。 AWS Batchであれば、 AWSBatchFullAccess
ポリシー内で、Batch関連のあらかじめ用意されたロールについて iam:PassRole
が許可されている。
sts:AssumeRole
sts:AssumeRole
は、ロールに対して付与する信頼ポリシーの中で指定できるアクションである。この「信頼ポリシー」は iam:PassRole
に登場した「アクセス許可ポリシー」とは概念が異なる。アクセス許可ポリシーがアイデンティティベースのポリシーなのに対し、信頼ポリシーはリソースベースのポリシーの一種である。アイデンティティベースのポリシーにはリソースネーム(ARN)が付与され、様々なアイデンティティに使い回してアタッチすることが可能だが、リソースベースは特定のリソースと1対1・不可分の関係である。
sts.AssumeRole
は、そのロールをどのリソースにAssume(引き受けさせる)できるかを定義する。もし Assume
を管理するという概念がなかった場合、例えばAWSサービスAのために強い権限を持つロールを作ったとして、そのロールを別のAWSサービスBに与えることができてしまう。その場合、サービスBが意図しない強い権限を持ち、セキュリティ上の問題となりうる。そこで、そのロールをどのリソースに渡すことができるか、ロール自体と不可分のインラインポリシーとして定義できるようになっている。もちろん sts.AssumeRole
を何も定義しない場合、そのロールはどのリソースにも渡せない。
iam:PassRole
の場合と同様に、AWSによってあらかじめ用意されているロールには、既に sts:AssumeRole
が定義されている。例えば AWSBatchServiceRole
には以下の信頼ポリシーが付与されており、もしこのポリシーがない場合は改めて作成してからロールを使うよう案内されている。
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "batch.amazonaws.com"}, "Action": "sts:AssumeRole" }] }
この信頼ポリシーにより、 AWSBatchServiceRole
はAWS Batch以外のAWSサービスには引き受けられないようになっている。
なお、 sts:AssumeRole
のPrincipalはAWSサービスのみではなく、他のAWSアカウントのユーザなどにもできる。
iam:PassRole
と sts.AssumeRole
により、「ユーザはどのロールを付与することができるか」「そのロールはどのリソースに付与されることができるか」の両面からロールを管理できるようになっている。ロールは通常であれば持っていない権限を一時的に付与するものであるため、強い権限が意図しない相手に渡らないようにするための仕組みが必要だったのだろう。
ロールを作って使うときの全体像は、以下のドキュメントがわかりやすい。
docs.aws.amazon.com
【AWS】CloudTrailログからリクエストのGlobal Condition Keyを推測する
AWSのポリシーを作るとき、 Condition
句を使って特定のプリンシパルをポリシー適用の例外として指定したい時がある。 Condition
句の中で指定した条件は、リクエストに含まれるGlobal Condition Keyと照らし合わせて確認され、リクエストが許可されるかどうかが決まる。
しかし、業務で特定のアカウントやVPC・サービスのみがS3バケットにアクセスできるようバケットポリシーを作ろうとした際、テストの方法がわからず、ポリシーを変えてはリクエストを送ってみて確認、という作業を繰り返すことになってしまった。
より効率的な方法はないかカスタマーサポートに聞いてみたところ、Global Condition Keyを直接確認できる方法は存在しないが、CloudTrailでリクエストログを取得していればそこから推測できると教えてもらった。
CloudTrailのログのフィールドは以下のドキュメントにまとまっている。
例えば、AWS glueサービスからS3バケットに置いてあるスクリプトをダウンロードしようとして、Access Deniedのエラーが出たとする。その場合、エラーメッセージに requestId
が表示されていれば、それを使ってCloudTrailのログを検索することで、当該リクエストの詳細なログを取得できる。
ポリシーの Condition
によく使うキーとの対応として、 aws:userid
は CloudTrailログの useridentity.principalId
に、 aws:PrincipalAccount
は userIdentity.accountId
に相当する。また、VPC Endpoint経由でのアクセスかどうか確認するには、CloudTrailログ内に vpcEndpointId
フィールドが存在するかを確認すればよい。
リクエストのログを取得することができれば、トライアンドエラーでポリシーを何度も変更する必要がなくなり、ポリシー設計が非常に楽になる。
【Terraform】AWS providerの認証で詰まった
先日リモート環境から、terraformで管理しているAWSのresourceを更新する作業を始めようとして terraform init
したところ、アクセスができずに詰まってしまった。
$ terraform init Initializing modules... Initializing the backend... Error refreshing state: AccessDenied: Access Denied status code: 403, request id: XXXXXXXXXXXXXXXX, host id: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
リモート環境でもawsに接続できるよう ~.aws/credentials
ファイルにaccess keyとsecretを入れておいたのだが、Terraformのドキュメントを見たところ、 ~.aws/credentials
を作るだけでは認証ができないことがわかった。
shared credentials fileで認証しようと思う場合、 provider
resourceの shared_credential_file
にファイルパスを渡しておかなければいけない。よく考えればTerraformで使えるbackendはAWSだけではないので、shared credentials fileを単に置いておいただけでデフォルトで読み取ってくれることはない。
今回はproviderの設定を変えることができない状況だったが、Terraformの認証では環境変数があればshared credentials fileよりも高い優先度で認証に使われる。 AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, AWS_DEFAULT_REGION
の環境変数を正しく設定すると terraform init
を行うことができた。
kubectlのコマンド補完を有効にする
kubectlを使っていると、current-contextの確認やnamespaceの切り替えなどでコマンドを打つのが面倒になってくる。こんなときはプラグインをインストールするのが一般的だが、その前に素の状態でできることはないか探してみたところ、kubectl自体に補完機能があることに気づいた。
補完機能はbashとzshに対応している。zshであれば、以下の1行を.zshrcに足せば良い。
source <(kubectl completion zsh)
kubectl completion zsh
は、zshで補完を行うためのスクリプトを自動生成している。
補完機能を有効にすると、例えば config
や current-context
などkubectlのサブコマンドや引数をTabで補完してくれるようになるため、タイピングが楽になる。ただし、namespaceやPodの名前等までは補完してくれない。
【Terraform】Dynamic BlockでModuleのArgumentを出し分ける
業務ではAWS Lambda FunctionをTerraformのModuleで管理しているが、先日Lambda Functionの1つに失敗時のアラートをつけるべく、dead letter queueを設定したいと思い立った。Terraformでは dead_letter_config
Argumentを使うことで設定することができる。
しかし、単に dead_letter_config
ArgumentをModule内に付け足してしまうと、全てのLambda Functionでdead letter queueの設定をしなければならなくなってしまう。かといってModuleの呼び出し先で特定のArgumentを追加する方法も見当たらず、他のLambda Functionに変更を及ぼさずに済む方法がないか詰まってしまった。
このような場合、Terraform v0.12以上であればDynamic Blockを使うことでArgument自体の出し分けが可能となる。
Dynamic Blockは、resource内でネストされるブロックに対して使うことで、同じ構造のブロックを動的に生成できるようになるもの。ネストしたブロックを使って記述するArgument限定ではあるが、引数を空にすることでそのブロックを生成させないこともできる。
今回の場合、Moduleに以下のような記述を追加した。
variable "dead_letter_config_target" { default = [] type = list description = "(Optional) arn of dead_letter_config's target" }
resource "aws_lambda_function" "lambda" { # other arguments dynamic "dead_letter_config" { for_each = toset(var.dead_letter_config_target) content { target_arn = dead_letter_config.value } }
Lambda FunctionのModuleの中で、 dead_letter_config
の設定をDynamic Blockで作るようにする。この中では、 dead_letter_config_target
で指定したリスト内の要素がループされ、 target_arn
の値として使われる。
そのうえで、dead_letter_queueを使いたいLambda Functionで、Module呼び出しの際に dead_letter_config_target
変数を指定する。
module "udomomo-lambda-function" { source = "../module/lambda" # specify other variable dead_letter_config_target = ["arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:target-sns"] }
これでこのLambda Functionのみ dead_letter_config
の設定が追加される。 dead_letter_config_target
変数を指定しなければ、他のLambda Functionには全く変更は生じない。
【CKAD】素のVimでYAMLファイルを扱う時のTips
CKADを受験しようと思い練習をしているが、YAMLファイルを扱うのが面倒になってきた。特にインデント調整にかなり精神力を削られる。
時間が足りないことで有名な試験なので何か便利な拡張ライブラリを探したいところだが、CKADの場合実行環境が運営側から与えられる形であり、おそらく素のVimを使わなければいけない。YAMLファイルの場合に役立ちそうなテクニックをまとめておく。
インデントを設定する
KubernetesのYAMLファイルはインデントがスペース2文字になっていることが多いが、Vimの初期設定ではタブ(8文字分)になっている。この設定はVimのコマンドで可能である。
:set et :set sw=2 ts=2 sts=2
:set et
でタブをスペースに変換し、 :set sw=2 ts=2 sts=2
で幅を2文字分にしている。 また :set autoindent
をつけると、行を改行したときもインデントが保持される。
(2020/10/19追記) :set autoindent
をつけると、KubernetesのドキュメントからYAMLをコピペしたときに自動でインデントが追加されてしまい、非常に見辛くなるので削除した。
行のインデントを調整する
コマンドモードの状態で >>
を押すと、カーソルがある行のインデントを1つ進めることができる。 <<
で戻すことができる。カーソルが行頭・行末になくても良いので、インデントを変えたいときに非常に便利。
また複数行のインデントを変えたいときは、ビジュアルモードで行を選択し shift + >
( shift + <
)とすれば良い。 .
で繰り返すことができる。
CKAD本番では、最初に簡単な.vimrcを作った方が良いかもしれない。
localstackでローカルにS3を建てる
先日、localstackの存在を知った。このライブラリはAWSの各サービスをローカルで再現できるもので、AWSを使った機能を開発環境下でテストするのに役立つ。
一部のサービスは有料版でないと使えないが、今回は無料で使えるS3の機能を試してみた。
localstackは既に公式Docker imageが用意されているので、docker-compose.yml
を作成すればすぐに使える。
version: "3" services: localstack: image: localstack/localstack:0.11.3 container_name: localstack ports: - 4566:4566 - 8080:8080 environment: - DEFAULT_REGION=ap-northeast-1 - SERVICES=s3 - DATA_DIR=/tmp/localstack/data volumes: - /Users/naoya-otani/.localstack:/tmp/localstack/
ポート 4566
は、localstack上で動く全てのAWSサービスのデフォルトのエンドポイントのポート ( EDGE_PORT
環境変数で設定を変更できる)。 ポート 8080
はGUIダッシュボードのデフォルトのエンドポイントである。 SERVICES
には使いたいサービス名を記載する。
今回はS3なので、データの永続化もしておきたい。 DATA_DIR
はlocalstackの中でデータを保持するディレクトリを指すが、このディレクトリはlocalstackコンテナ内のものなので、 volumes
においてこのディレクトリとホスト側のディレクトリをマウントする必要がある。
docker-compose up -d
で起動したら、 http://localhost:4566
をエンドポイントとしてローカルのS3にアクセスできる。
ローカルのS3も、aws-cliコマンドで操作することができる。 --endpoint-url
オプションに上記のエンドポイントを指定することで、localstackのS3に対してコマンドを実行することができる。 AWS_ACCESS_KEY_ID
と AWS_ACCESS_SECRET_KEY
は任意の値で良い。ただし、 --endpoint-url
をつけ忘れて間違ってリモートのS3にコマンド実行してしまう事故を防ぐため、まずlocalstack用のprofileを作成しておく方が良いだろう。(以下では両方とも値を dummy
にしている)
$ cat >> ~/.aws/credentials << EOF [localstack] aws_access_key_id = dummy aws_secret_access_key = dummy EOF $ cat >> ~/.aws/config << EOF [profile localstack] region = ap-northeast-1 output = json EOF $ aws configure list --profile localstack Name Value Type Location ---- ----- ---- -------- profile localstack manual --profile access_key ****************ummy shared-credentials-file secret_key ****************ummy shared-credentials-file region ap-northeast-1 config-file ~/.aws/config
profileを作成したら、localstackにS3のバケットを作成する。リモートのS3を操作する場合と同様、 s3api list-buckets
コマンドでバケットを確認できる。
$ aws s3 mb s3://sample-bucket --endpoint-url=http://localhost:4566 --profile localstack make_bucket: sample-bucket $ aws s3api list-buckets --endpoint-url=http://localhost:4566 --profile localstack { "Buckets": [ { "Name": "sample-bucket", "CreationDate": "2020-08-02T11:42:51.547110Z" } ], "Owner": { "DisplayName": "webfile", "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxx" } }
sample-bucket
にファイルをアップロードする。 s3 ls
コマンドでファイルが確認できれば成功。
$ aws s3 cp sample.txt s3://sample-bucket/sample-dir/ --endpoint-url=http://localhost:4566 --profile localstack upload: ./sample.txt to s3://sample-bucket/sample-dir/sample.txt $ aws s3 ls s3://sample-bucket/sample-dir/ --endpoint-url=http://localhost:4566 --profile localstack 2020-08-02 21:27:04 15 sample.txt