Google Apps ScriptでChrome Enterpriseのポリシーを弄る【GAS】
現在、Chrome Enterpriseの様々なポリシーを管理コンソール上で手動で設定しています。その中でドッペルゲンガードメインへのアクセスを封じる為に、URLブロック機能を活用しています。
このURLブロックのリストをGASで編集出来ないか?と考え調べてみると、REST APIを叩くことでそれが実現できることがわかったので、今回チャレンジしてみることにしました。
目次
今回利用するファイル等
- Chrome Policy API
- ポリシーを弄るスプレッドシート - Google Spreadsheet
- Chrome Enterprise Policy - URLBlockList
- URLブロック - 管理コンソール
Google Chromeに対する様々なポリシーを適用し、Google Workspaceに於いてユーザのChromeの厳格管理を行うことのできるChrome Enterpriseですが、そのポリシー設定については以下のエントリーにまとめてあります。非常に多種多様なポリシーがありますが、今回はこの中にあるURLブロックリストのURLを編集する操作を行います。
最大1000個まで登録可能です。
事前準備
今回利用するAPIはGCP側のAPIとなるため、結構な準備が必要になります。事前にChrome EnterpriseのURLのブロックに於いて、ブロックされるURLを複数登録してあります。
図:このブロックリストを操作します
プロジェクトの移動
ちょっと特殊なAPIの利用となるので、Google Apps ScriptのプロジェクトをGCPと連結する必要があります。
図:プロジェクト変更画面
APIの有効化
前述のプロジェクト番号のプロジェクトに於いて、
GCP側で今回利用するAPIを有効化する必要性があります。
- GCPを開き、サイドバーからAPIとサービスを開きます。
- 上部にある「APIとサービスの有効化」をクリック
- 「admin」を検索し、Admin SDK APIクリックします。
- 有効化をクリックします。
- 同じく、次に「Chrome」を検索し、Chrome Policy APIをクリックします。
- 有効化をクリックします。
図:GCPのAPIを有効化しておく
appscript.jsonの編集
プロジェクト移動とAPIの有効化だけではどうにもうまく動かなかったのですが、appscripts.jsonを弄って手動でスコープの追加が必要だったようです。
スクリプトエディタの左サイドバーから「プロジェクト設定」を開き、「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れて、appsscript.jsonを表示する。その後そのファイルを開き、以下のように記述を行います。必須の作業です。これをしてしまうと、今後メソッドを追加したときに追加の認証は手動で、Scopeを入れてあげないと認証されないので要注意。
最後に適当に関数を実行して認証をする必要があります。
1 2 3 4 5 6 7 8 |
"oauthScopes": [ "https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/chrome.management.policy", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/admin.directory.user", "https://www.googleapis.com/auth/admin.directory.orgunit" ] |
※https://www.googleapis.com/auth/chrome.management.policyが特に重要です。
拡張サービスを有効化する
今回、GASからAdmin SDKを直接利用するので、以下の作業が必要になります。
- スクリプトエディタを開く
- 左サイドバーのサービスにある+をクリックする
- Admin SDKを選択して、追加をクリック
図:Directory APIがこれで使えるようになる
操作するスキーマ名を調べる
ここがちょっと厄介なのですが、今回利用するのはURLブロックという項目を操作します。この際に利用するスキーマ名称は「chrome.users.UrlBlocking」となるのですが、これがどこにも記載がありません。
調べ方の手順は以下のとおりです。
設定の箇所をまず調べる
今回の設定箇所は、管理コンソール上ではこちらのページになりますが、Chrome Enterprise上ではユーザとブラウザの設定=>URLのブロックという場所になります。
そこから、こちらのページにある「スキーマの名前空間」から見てみると、「chrome.users.LEAF_NAME」ということがわかります。次にこのLEAF_NAMEを調査する必要があります。
Claudeのチャットで聞いてみたら割といい線まで調べられました。
図:設定箇所はこちらの項目
図:Claudeで聞いてみた
LEAF NAMEを特定する
さてURLのブロックに該当するLEAF NAMEというものを調べる必要があるのですが、以下の手順で調べます。
- こちらのページを開く
- 検索窓から「urlblockやブロック」と検索してみると左サイドバーの中にURLBlocklistという名称がそれっぽいので開いてみる
- 中を確認してURLのブロックに関するものであると確認する。
- 次にこちらのページを開きます。
- F3の検索窓でページを検索にて、URLBlockListを検索する
- すると1つ目に合致するものが見つかるので、そこに記載されている「chrome.users.UrlBlocking」がLEAF NAMEを含めたスキーマ名なので控えておく
図:スキーマ名が見つかった
GASにスキーマ名を追記する
取得できたchrome.users.UrlBlockingというスキーマ名を今回のサンプルに用意したスプシのGASの一番上にあるschemename変数に対して登録します。これを利用することで対象のスキーマに対しての操作が可能になります。
図:スキーマ名を入力しておく
ソースコード
概要
今回利用するChrome Policy APIは、GASから直接的に扱えるメソッドは用意されていません。よって、UrlfetchAppを利用してHTTPリクエストにて操作する必要があります。リクエストするcurlのサンプルはGoogle公式のGithubリポジトリに詳しく掲載されているので、これをベースにGASで構築することになります。
今回特に利用するリクエスト方法は、「List policies for an Organizational Unit」などが該当します。各スキーマによって、値の取得法や割当法、スキーマ名称が異なったりするので、コードを書くよりも特定するほうが厄介です。
※今回スプシへのデータの書き出しや、スプシから反映といった部分は省略していますが、ここを実装することでURLのブロックリストのメンテナンスを自動化することが可能になります。
図:リクエスト方法
現在のブロックリストを取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
//操作するスキーマ名 var schemename = "chrome.users.UrlBlocking"; //URLBlockListを取得する function getUrlBlockList() { //アクセストークンを取得 let token = ScriptApp.getOAuthToken(); //ログインユーザの情報にドメイン情報を全部取得する let mail = Session.getActiveUser().getEmail(); let user = AdminDirectory.Users.get(mail); let customerId = user.customerId //ルートの組織部門のIDを取得する let rootid = rootOrgUnitId(customerId); //リクエストURL let url = "https://chromepolicy.googleapis.com/v1/customers/" + customerId + "/policies:resolve"; //リクエストボディ let payload = { "policySchemaFilter": schemename, "policyTargetKey": { "targetResource": "orgunits/" + rootid }, "pageToken": "" } //HTTPリクエスト let response = UrlFetchApp.fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + token }, contentType: "application/json", payload : JSON.stringify(payload) }); //リクエスト結果を取得 let result = JSON.parse(response.getContentText()); //ブロックリストを取得する console.log(result.resolvedPolicies[0].value.value.urlBlocklist); } //ルートの組織部門のIDを取得する function rootOrgUnitId(customerId) { //ルート以下の組織部門一覧を取得する let page = AdminDirectory.Orgunits.get(customerId, "/") let tempid = page.organizationUnits[0].parentOrgUnitId; //冒頭の文字を削る let orgunitroot = tempid.replace("id:","") //値を返す return orgunitroot } |
- リクエストエンドポイントは「https://chromepolicy.googleapis.com/v1/customers/{customerId}/policies:resolve」となります。
- {customerId}の部分は、管理コンソール上でも確認出来ますが、GWSの顧客IDになります。これをコードで取得して割り当ててます。
- リクエスト時に適用対象にしてる組織部門のIDが必要です。今回はルートの組織部門のIDを取得する為にrootOrgUnitId関数を用意しています。
- ただし、AdminDirectoryでは子組織部門のIDはとれても、ルートの組織部門のIDは取得が出来ないので、1つ目の子組織部門のparentOrgUnitIdをもってルートの組織部門IDとして取得しています。
- また、この時のIDの文字列の前にid:という余計な文字が含まれてるのでreplaceで削っています。
- POSTにてリクエストボディを構築しリクエストを送ります。
- URLのブロックの場合、取得した結果のresult.resolvedPolicies[0].value.value.urlBlocklistの場所に、ブロックするURLのリストが可能されているのでそれを取得します。
実際にこのコードで実行すると、ブロックするURLのリストが配列で返ってきます。
図:無事に取得出来ました。
ブロックリストを更新する
今回既存のブロックするURLのリストに「facebook.com」を新たに、真ん中より下あたりに追加してみたいと思います。これで追加されることで、ユーザはfacebook.comへのアクセスがブロックされるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
//URLBlocklistを更新する function updateBlocklist(){ //アクセストークンを取得 let token = ScriptApp.getOAuthToken(); //ログインユーザの情報にドメイン情報を全部取得する let mail = Session.getActiveUser().getEmail(); let user = AdminDirectory.Users.get(mail); let customerId = user.customerId //ルートの組織部門のIDを取得する let rootid = rootOrgUnitId(customerId); //リクエストURL let url = "https://chromepolicy.googleapis.com/v1/customers/" + customerId + "/policies/orgunits:batchModify"; //ブロックするURLのリスト //facebook.comを追加してみた let blockarr = [ 'goggle.com', 'yahooo.jp', 'icroud.com', 'bit.ly', 'x.gd', 'bit.do', 'ux.nu', 'fb.me', '00m.in', 'rnicrosoft.com', 'ru', 'cn', 'ci', 'top', '.sign-in.info', 'rakutencojp.com', 'googIe.com', 'facebook.com', 'amazoon.com', 'amazzon.com' ] //リクエストボディ let payload = { "requests": [ { "policyTargetKey": { "targetResource": "orgunits/" + rootid }, "policyValue": { "policySchema": schemename, "value": {urlBlocklist: blockarr} }, "updateMask": {paths:"urlBlocklist"} } ] } //HTTPリクエスト let response = UrlFetchApp.fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + token }, contentType: "application/json", payload : JSON.stringify(payload) }); //リクエスト結果を取得 let rescode = response.getResponseCode(); console.log(rescode) let result = JSON.parse(response.getContentText()); console.log(result) } |
- リクエストエンドポイントは「https://chromepolicy.googleapis.com/v1/customers/{customerId}"/policies/orgunits:batchModify」になります。
- {customerId}の部分は、管理コンソール上でも確認出来ますが、GWSの顧客IDになります。これをコードで取得して割り当ててます。
- 組織部門ID周りのお話は前述の取得の項目と同じです。
- ブロックするURLのリストは1次元配列で作成します。
- payloadの部分が重要で、valueの中の項目やupdateMaskの項目がURLBlocklistの頭の文字は小文字にする必要があります(これをキャメルケースと呼びます)。
- urlBlocklistにブロックするURLの1次元配列をそのまま割り当てます。
- POSTにてリクエストし成功するとresponseCodeは200が返ってきます。
ここで成功したら、管理コンソールのURLのブロックの中身をリロードすると、きちんとfacebook.comが追加されてるのを確認できます。
図:無事に成功しました
図:更新されたリスト
図:バッチリブロック出来ました
スキーマ名検索
おまけですが、今回スキーマ名を調べるに当たって利用したスキーマ名検索用のコードです。注意点としては、policynameに検索するポリシーの名前を入れておくのですが、URLBlocklistならば頭のURL部分は小文字にする必要があります。
検索を実行してみると、これにヒットする項目が3つ出てきました。ここから推測して今回利用しています。
図:3つの該当するスキーマ名が出てきた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
//Chrome EnterpriseのSchemeリスト //https://chromeenterprise.google/policies/#URLBlocklist function getSchemelist(){ //アクセストークンを取得 let token = ScriptApp.getOAuthToken(); //ログインユーザの情報にドメイン情報を全部取得する let mail = Session.getActiveUser().getEmail(); let user = AdminDirectory.Users.get(mail); let customerId = user.customerId //対象のポリシー名 let policyname = "urlBlocklist" //URLBlocklistならば、冒頭のURLは小文字にする //リクエストURL let url = "https://chromepolicy.googleapis.com/v1/customers/" + customerId + "/policySchemas?pageSize=100&pageToken&filter=field_descriptions.field=" + policyname //HTTPリクエスト let response = UrlFetchApp.fetch(url, { method: 'GET', headers: { Authorization: 'Bearer ' + token }, contentType: "application/json" }); //リクエスト結果を取得 let result = response.getContentText(); //policyを表示 let temppolicy = JSON.parse(result); let scheme = temppolicy.policySchemas; let length = scheme.length; //スキーマを調べる for(let i = 0;i<length;i++){ let rec = scheme[i]; console.log(rec.schemaName) } } |
関連リンク
- Phishing URL dataset from JPCERT/CC
- google/ChromeBrowserEnterprise
- ポリシー スキーマについて
- URLAllowlist not working
- Admin Console - Method: orgunits.get
- Getting list of child OU in Apps Script
- Directory API: リクエストの承認
- Unable to update some chrome policies #412
- Chrome Policies : taers232c/GAMADV-XTD3
- URL Blocking GAM Commands
- 命名規則(キャメルケース, パスカルケース, スネークケース, ケバブケース)
- Chrome ブラウザ クラウド管理 API を使用する