Cloudflare Access を再利用可能なポリシーに移行する

# 目的
Cloudflare Access ポリシーが再利用可能になったので、既存の `LEGACY` ポリシーを再利用可能なポリシーに移行します。

- [Access New feature - Unified Application Definition and Reusable Policies - Zero Trust / Access - Cloudflare Community](https://community.cloudflare.com/t/access-new-feature-unified-application-definition-and-reusable-policies/759433)

>アクセスポリシーを独自のオブジェクトにすることで、複数のアプリケーションで再利用できるようになりました。これらのアップデートは、Access Dashboardのエクスペリエンス全体の大幅なアップデートを伴うものでした。
>
>**アクセス・ポリシーが再利用可能に**
>
>以前は、アクセス・ポリシーは常に特定のアプリケーションにスコープされていました。このため、アプリケーションごとに個別のポリシーを定義する必要がありました。
>
>再利用可能なAccessポリシーへの移行の一環として、既存のすべてのAccessポリシーは、アプリケーションにスコープされたままの「レガシー」ポリシーとして残ります。「レガシー」は、新しいポリシータブで読み取り専用として表示されます。
>
>APIでは、レガシーポリシーを再利用可能なポリシーに変換するための追加オプションを1つ使用できます。
>
>次のエンドポイントをPUTで使用してください: `https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/{appID}/policies/{legacy_policy_id}/make_reusable`
>
>リクエストボディは空でなければなりません。
>
>これはレガシーポリシーを再利用可能なポリシーに変換し、複数のアプリケーションに割り当てることができます。一度変換されたポリシーは、再利用可能なポリシーのエンドポイントを通してのみ編集できます (つまり、`PUT /apps/id/policies/id` を通してポリシーを編集することはできなくなります)。

# 再利用可能なポリシー
`LEGACY` ポリシーは、1つの Access Application のみに紐付けて利用する形でした。

そのため、同じポリシー内容であっても、複数回定義しなければならない手間がありました。

```mermaid:
%%{init: {'themeCSS': 'img {width: 48px; height:48px;}'}}%%
graph TB

subgraph Access [Cloudflare Access - LEGACY Policies]
direction TB
App1[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Application 1]:::transparent
App2[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Application 2]:::transparent
P1[<img src='https://cf-icons.pages.dev/rules.svg' width='48' height='48'/><br>Policiy 1]:::transparent
P2[<img src='https://cf-icons.pages.dev/rules.svg' width='48' height='48'/><br>Policiy 2]:::transparent
P3[<img src='https://cf-icons.pages.dev/rules.svg' width='48' height='48'/><br>Policiy 3]:::transparent

App1 --- P1
App2 --- P2
App2 --- P3
end

classDef hidden display: none;
classDef transparent fill-opacity:0,stroke-opacity:0;
style Access fill-opacity:0,stroke-opacity:0;
```

>このポリシーはアプリケーションの一部として作成されました。再利用はできません。このポリシーを編集するには、対応するアプリケーションに移動します。これらのルールを複数のアプリケーションで使用するには、再利用可能な新しいポリシーを作成することをお勧めします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/01016ccc-0542-25b5-bf9d-5c22e961715f.png)

一方、再利用可能なポリシーは、複数の Access Application で何度も再利用が可能で、使い回しがききます。

```mermaid:
%%{init: {'themeCSS': 'img {width: 48px; height:48px;}'}}%%
graph TB

subgraph Access [Cloudflare Access - Reusable Policies]
direction TB
App1[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Application 1]:::transparent
App2[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Application 2]:::transparent
P1[<img src='https://cf-icons.pages.dev/rules.svg' width='48' height='48'/><br>Policiy 1]:::transparent
P2[<img src='https://cf-icons.pages.dev/rules.svg' width='48' height='48'/><br>Policiy 2]:::transparent
App3[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Application 3]:::transparent

App1 --- P1
App2 --- P1
App3 --- P1
App2 --- P2
App3 --- P2

end

classDef hidden display: none;
classDef transparent fill-opacity:0,stroke-opacity:0;
style Access fill-opacity:0,stroke-opacity:0;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f59d341c-5538-b2d4-fe58-377d4370d214.png)


# レガシーポリシーからの移行
既存の `LEGACY` ポリシーを再利用可能にするには、移行の作業が必要となります。

## 1つずつ移行
`Application ID` と `Policy ID` を指定して、以下のエンドポイントに対して `PUT` コールをします。

```shell:
export EMAIL='YOUR_EMAIL'
export APIKEY='YOUR_APIKEY'
export ACCOUNT_ID='YOUR_ACCOUNT_ID'
export LEGACY_APP_ID='YOUR_LEGACY_APP_ID'
export POLICY_ID='YOUR_POLICY_ID'

curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/$LEGACY_APP_ID/policies/$POLICY_ID/make_reusable" \
     -H "X-Auth-Email: $EMAIL" \
     -H "X-Auth-Key: $APIKEY" \
     -H "Content-Type: application/json"
```

`"reusable": true,` となったレスポンスが得られ、ポリシーが再利用可能になったことが確認できます。

```json:result.json
{
  "result": {
    "created_at": "2024-04-12T07:06:22Z",
    "decision": "allow",
    "exclude": [],
    "id": "d7eff9fc-e44f-44f8-b305-39150ae5f708",
    "include": [
      {
        "login_method": {
          "id": "d973f7d7-40d0-4c55-86cd-f79b08ce8f7c"
        }
      }
    ],
    "name": "Cognito group Allow",
    "require": [
      {
        "oidc": {
          "identity_provider_id": "d973f7d7-40d0-4c55-86cd-f79b08ce8f7c",
          "claim_name": "cognito:groups",
          "claim_value": "Dodgers"
        }
      }
    ],
    "uid": "d7eff9fc-e44f-44f8-b305-39150ae5f708",
    "updated_at": "2024-04-12T07:07:35Z",
    "reusable": true,
    "app_count": 1
  },
  "success": true,
  "errors": [],
  "messages": []
}
```

## 一括で移行

以下のスクリプトによって、アカウント内のすべての `LEGACY` ポリシーを再利用可能にします。

https://zenn.dev/sho7650/articles/6caab13d183471

https://qiita.com/re-sasaki/items/7d34109cf209a8ac753c

```shell
export EMAIL='YOUR_EMAIL'
export APIKEY='YOUR_APIKEY'
export ACCOUNT_ID='YOUR_ACCOUNT_ID'

curl -sX GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/" \
     -H "X-Auth-Email: $EMAIL" \
     -H "X-Auth-Key: $APIKEY" \
     -H "Content-Type: application/json" | jq -c '.result[] | select(.type != "bookmark" and .type != "private_ip")' |
while read -r application; do
  APP_ID=$(echo "${application}" | tr -d '[:cntrl:]' | jq -r '.id')
  echo "${application}" | tr -d '[:cntrl:]' | jq -c '.policies[] | select(.reusable == false)' |
  while read -r policy; do
  POLICY_ID=$(echo "${policy}" | jq -r '.id')
  REUSABLE=$(echo "${policy}" | jq -r '.reusable')
  echo APP_ID = ${APP_ID},  POLICY_ID = ${POLICY_ID} , REUSABLE = ${REUSABLE} です
  curl -sX PUT "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/$APP_ID/policies/$POLICY_ID/make_reusable" \
     -H "X-Auth-Email: $EMAIL" \
     -H "X-Auth-Key: $APIKEY" \
     -H "Content-Type: application/json" | jq -c '.result | {"id","name","reusable"}'
  done
done
```

```shell:result.txt
APP_ID = fdb959cf-e4a7-41c5-b0f1-f1746b9d6659, POLICY_ID = 7f0333d9-8ffb-4522-a09d-d641baf18733 , REUSABLE = false です
{"id":"7f0333d9-8ffb-4522-a09d-d641baf18733","name":"sso allow","reusable":true}
APP_ID = a722c7ea-6a9e-4520-a621-cfa9bff16ed6, POLICY_ID = bfba49dd-40ee-4c13-a399-8e3ab56bfc8a , REUSABLE = false です
{"id":"bfba49dd-40ee-4c13-a399-8e3ab56bfc8a","name":"service token","reusable":true}
APP_ID = a722c7ea-6a9e-4520-a621-cfa9bff16ed6, POLICY_ID = 81283344-3f7b-45e6-9ad1-12ccb642c3af , REUSABLE = false です
{"id":"81283344-3f7b-45e6-9ad1-12ccb642c3af","name":"ext","reusable":true}
APP_ID = a722c7ea-6a9e-4520-a621-cfa9bff16ed6, POLICY_ID = 089b406e-a927-4024-8809-3c60d4d84667 , REUSABLE = false です
{"id":"089b406e-a927-4024-8809-3c60d4d84667","name":"idp-mtls","reusable":true}
...
```

# まとめ

新規で作成するポリシーはデフォルトで再利用可能ですが、既存の `LEGACY` ポリシーは UI で再作成するか、API で移行する作業が必要です。

今回の作業で Cloudflare Access の、既存の `LEGACY` ポリシーを再利用可能なポリシーに移行できました。

これで、新しいポリシー管理の方法で、柔軟に構成する準備を整えることができましt。