API Gateway利用時にAPI作成削除権限だけを除外した編集権限の作成

# はじめに
API Gatewayをプライベートなネットワークで利用したい場合、REST API(Private)を利用する案が出てくるかと思います。

その時、すべてのユーザーにAPIを自由に作成・削除できる権限を与えてしまうと、REST API(Private)以外のプロトコル・エンドポイントタイプで作成されてしまう可能性があります。

そのため、API自体の作成・削除は管理者が行い、他のユーザーはAPI内の変更のみできる権限を検証しました。

以下のドキュメントに記載されているポリシーを参考にしました。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/security_iam_id-based-policy-examples.html#security_iam_id-based-policy-examples-private-api

# 作成ポリシー
実際に作成したポリシーは以下になります。
読み取り権限やAPI実行権限は別途付与されている想定として除外しています。
```
{
    "Statement": [
        {
            "Action": [
                "apigateway:PUT",
                "apigateway:POST",
                "apigateway:PATCH",
                "apigateway:DELETE"
            ],
            "Condition": {
                "ForAllValues:StringEqualsIfExists": {
                    "apigateway:Request/EndpointType": "PRIVATE",
                    "apigateway:Resource/EndpointType": "PRIVATE"
                }
            },
            "Effect": "Allow",
            "Resource": [
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*"
            ],
            "Sid": "APIGatewayAllowPolicy"
        }
    ],
	"Version": "2012-10-17"
}
```

# 背景
以下のドキュメントを見ていただくとわかりますが、API Gateway向けに定義されているアクションはそれほど多くはありません。

https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazonapigatewaymanagement.html

それらのアクションの中でもCreateRestApiやPutMethodなどのアクションは主に以下の5つのアクションに統合されています。

* GET
* POST
* PATCH
* PUT
* DELETE

そのため、CreateRestApiやDeleteRestApiだけ拒否するといった細かい権限制御がしづらくなっており、実現するために色々と検証したので、せっかくなので残しておこうと思ったのでこの記事書いています。


# 検証時の経緯
最初はAllowの権限を付与した上でAPI作成・削除機能のみDenyするアプローチを取っていたのですが、上述した細かい権限制御がしづらい理由から想定外の操作もDenyされてしまい、中々やりたいことが実現できませんでした。

そこでDenyするアプローチから、Allowで許可する範囲を限定するアプローチに変更したところ、これがうまくいきました。
特に工夫した箇所は以下のResource部分になります。

```
            "Resource": [
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*"
            ],
```


Rource部分を実装する上で確認したのは以下のドキュメントです。

>Amazon API Gateway Management で定義されるリソースタイプ
https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazonapigatewaymanagement.html

例としてDELETEアクションを見ていただくとわかるのですが、API Gatewayは1つのアクションに対して複数の操作権限が混在しているので、アクションで指定できるリソースタイプも複数あります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ddb2b40e-140f-0ba6-2048-8127ee791e5f.png)

そして各リソースタイプで指定できるARNの値を見てみると、かなり細かいARN構造となっていることがわかります。
その中でRest APIのARN構造を見てみると、以下のように最初に「restapis」があり、メソッドやステージなどのAPIを構成する要素は「restapis」の後ろに付与されるようになっていました。
```
arn:${Partition}:apigateway:${Region}::/restapis
arn:${Partition}:apigateway:${Region}::/restapis/${RestApiId}
arn:${Partition}:apigateway:${Region}::/restapis/${RestApiId}/resources
arn:${Partition}:apigateway:${Region}::/restapis/${RestApiId}/models
arn:${Partition}:apigateway:${Region}::/restapis/${RestApiId}/stages
・
・
・
```

その構造から、恐らくRest API作成削除時には以下のリソースタイプを対象としてCreateRestApiやDeleteRestApiが実行されると想定しました。
それを前提とすると、ポリシーのResource指定でそれらを含めなければ、リクエストに対してAllow権限を判定する際にResourceの記述が足りていないため、API作成削除操作ができなくなると考えました。

```
arn:${Partition}:apigateway:${Region}::/restapis
arn:${Partition}:apigateway:${Region}::/restapis/${RestApiId}
```

また、操作させたくないのはRest APIの作成と削除のみでメソッドの作成やステージの更新などはさせたいため、それを踏まえて以下のように設定しRest API内の要素は操作できるように実装をしました。

```
            "Resource": [
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*/*",
                "arn:aws:apigateway:ap-northeast-1::/restapis/*/*"
            ],
```

ConditionでリクエストとレスポンスのエンドポイントタイプがPrivateであることを指定しているので、これでRest API(Private)の要素のみの編集が可能な権限の完成です。

```
            "Condition": {
                "ForAllValues:StringEqualsIfExists": {
                    "apigateway:Request/EndpointType": "PRIVATE",
                    "apigateway:Resource/EndpointType": "PRIVATE"
                }
            },
```


# おわりに
ユースケースとしてはあまりない内容な気がするので、果たして他の方の参考になるかはわからないのですが、この記事がどなたかの参考になれば幸いです。