レコードセットに対する Cloud DNS のきめ細かい IAM 権限


この Codelab では、IAM 条件とカスタムロールを使用して、Cloud DNS 内の個々の DNS レコードセットにきめ細かいアクセス制御を実装する方法について説明します。

はじめに

Cloud DNS は、従来からプロジェクト レベルとマネージド ゾーンレベルでの IAM 権限の設定をサポートしています。これにより、ゾーン内のすべてのレコードに幅広くアクセスできます。ただし、大企業の場合、これは「最小権限の原則」を満たしていません。

この Codelab では、Cloud DNS でレコードセットごとの IAM 権限を構成する方法について説明します。この機能を使用すると、特定のサブドメインまたはレコードタイプの管理を、単一の共有マネージド ゾーン内の異なるチームに委任できます。

学習内容

  • IAM 条件を使用して DNS レコードの管理を制限する方法。
  • カスタムロールを作成する理由と方法。
  • サブドメインと特定のレコードタイプ(A、MX など)を委任する方法。
  • --skip-soa-update フラグを使用して、条件付き権限で DNS トランザクションを処理する方法。

前提条件

  • Google アカウント
  • 課金を有効にした Google Cloud プロジェクト
  • Google Cloud CLI の最新バージョンがインストールされ、構成されている
  • DNS と IAM のコンセプトに関する基本的な知識

設定方法

所要時間: 03:00

Cloud DNS API を有効にする

gcloud CLI にログインして、API を有効にします。

gcloud auth login
gcloud services enable dns.googleapis.com

テスト プロジェクトを作成する

プロジェクトがまだない場合は、今すぐ作成して、すべてのコマンドがそのプロジェクトを対象とするように gcloud 構成を設定します。

gcloud projects create my-dns-per-rrset-lab
gcloud config set project my-dns-per-rrset-lab

アプローチについて

所要時間: 02:00

DNS レコードを正常に変更するには、プリンシパルに、変更リソースに対するレコードの変更と関連オペレーションの両方を実行する権限が必要です。

これは、次のいずれかの方法で構成できます。

IAM 条件は、標準の roles/dns.admin ロールに直接バインドできます。レコードの変更と Change リソースに対するオペレーションの両方をサポートするには、必要な非レコードセット リソースへのアクセス権を付与する、より包括的な CEL 条件を使用します。

  • 許可型: 他のすべての DNS 管理アクションを許可します。補完的なロールに他のすべての標準管理権限が含まれている場合、これはオプション 2 と同等です。 cel (resource.type == 'dns.googleapis.com/ResourceRecordSet' && <RRSET_CONDITION>) || (resource.type != 'dns.googleapis.com/ResourceRecordSet')
  • 制限付き: レコードセットの変更と Change リソースに対するオペレーションのみを許可します。レコードセットの一覧表示(dns.resourceRecordSets.list)やマネージド ゾーンの説明など、他の管理アクションはブロックされます。 cel (resource.type == 'dns.googleapis.com/ResourceRecordSet' && <RRSET_CONDITION>) || (resource.type == 'dns.googleapis.com/Change')

この方法ではカスタムロールの作成と管理が不要になるため、この方法をおすすめします。

オプション 2: カスタムロール アプローチ

条件を簡素化する場合は、2 つのカスタムロールを使用して、レコード セットの管理を他の管理タスクから分離できます。

  1. DnsRecordSetAdmin: リソース レコードセットの作成、削除、取得、更新を行う権限が含まれています。このロールは条件付きで付与されます。
  2. DnsNonRecordSetAdmin: 他のすべての DNS 管理権限(ゾーンの管理、レコードの一覧表示、プロジェクトの詳細の表示など)が含まれます。このロールは無条件で付与されます。

カスタムロールの作成

所要時間: 05:00

[!NOTE] この手順は、オプション 2: カスタムロール アプローチを使用している場合にのみ必要です。オプション 1: 標準ロール アプローチを使用している場合は、この手順をスキップできます。

次のコマンドを実行して、プロジェクトに必要なカスタムロールを作成します。

権限セットを定義する

# Record set management permissions
rs_perms="dns.resourceRecordSets.create,dns.resourceRecordSets.delete,dns.resourceRecordSets.get,dns.resourceRecordSets.update"

# Complementary administrative permissions
comp_perms="compute.networks.get,compute.networks.list,dns.changes.create,dns.changes.get,dns.changes.list,dns.dnsKeys.get,dns.dnsKeys.list,dns.gkeClusters.bindDNSResponsePolicy,dns.gkeClusters.bindPrivateDNSZone,dns.managedZoneOperations.get,dns.managedZoneOperations.list,dns.managedZones.create,dns.managedZones.delete,dns.managedZones.get,dns.managedZones.getIamPolicy,dns.managedZones.list,dns.managedZones.update,dns.networks.bindDNSResponsePolicy,dns.networks.bindPrivateDNSPolicy,dns.networks.bindPrivateDNSZone,dns.networks.targetWithPeeringZone,dns.networks.useHealthSignals,dns.policies.create,dns.policies.createTagBinding,dns.policies.delete,dns.policies.deleteTagBinding,dns.policies.get,dns.policies.list,dns.policies.listEffectiveTags,dns.policies.listTagBindings,dns.policies.update,dns.projects.get,dns.resourceRecordSets.list,dns.responsePolicies.create,dns.responsePolicies.delete,dns.responsePolicies.get,dns.responsePolicies.list,dns.responsePolicies.update,dns.responsePolicyRules.create,dns.responsePolicyRules.delete,dns.responsePolicyRules.get,dns.responsePolicyRules.list,dns.responsePolicyRules.update,resourcemanager.projects.get"

Gcloud でロールを作成する

gcloud iam roles create DnsRecordSetAdmin --project=$(gcloud config get-value project) \
    --title="DNS Record Set Admin (Conditional)" --permissions="${rs_perms}"

gcloud iam roles create DnsNonRecordSetAdmin --project=$(gcloud config get-value project) \
    --title="DNS Complimentary Admin" --permissions="${comp_perms}"

シナリオ 1: 完全一致

所要時間: 05:00

このシナリオでは、チームに api.example.com.A レコードのみを管理する権限を付与します。

マネージド ゾーンを作成する

gcloud dns managed-zones create example-zone \
    --description="Lab zone for per-RRSet permissions" \
    --dns-name=example.com. --visibility=private \
    --networks=default

テスト サービス アカウントを作成する

このサービス アカウントを使用して、制限付き権限を検証します。

gcloud iam service-accounts create dns-restricted-sa \
    --display-name="Restricted DNS SA"

SA_EMAIL="dns-restricted-sa@$(gcloud config get-value project).iam.gserviceaccount.com"

条件付き IAM ポリシーを適用する

次のいずれかのオプションを選択して、ポリシーを適用します。

このオプションでは、制限のない条件で標準の dns.admin ロールを使用します。

cat << EOF > policy.json
{
  "bindings": [
    {
      "role": "roles/dns.admin",
      "members": ["serviceAccount:${SA_EMAIL}"],
      "condition": {
        "expression": "(resource.type == 'dns.googleapis.com/ResourceRecordSet' && resource.name.endsWith('/rrsets/api.example.com./A')) || (resource.type != 'dns.googleapis.com/ResourceRecordSet')",
        "title": "Exact Record Match (Standard Role)"
      }
    }
  ],
  "version": 3
}
EOF

gcloud dns managed-zones set-iam-policy example-zone --policy-file=policy.json

オプション 2: カスタムロール アプローチ

このオプションでは、前の手順で作成したカスタムロールを使用します。

cat << EOF > policy_custom.json
{
  "bindings": [
    {
      "role": "projects/$(gcloud config get-value project)/roles/DnsRecordSetAdmin",
      "members": ["serviceAccount:${SA_EMAIL}"],
      "condition": {
        "expression": "resource.type == 'dns.googleapis.com/ResourceRecordSet' && resource.name.endsWith('/rrsets/api.example.com./A')",
        "title": "Exact Record Match (Custom Roles)"
      }
    },
    {
      "role": "projects/$(gcloud config get-value project)/roles/DnsNonRecordSetAdmin",
      "members": ["serviceAccount:${SA_EMAIL}"]
    }
  ],
  "version": 3
}
EOF

gcloud dns managed-zones set-iam-policy example-zone --policy-file=policy_custom.json

制限を確認する

許可されたレコードを作成してから、許可されていないレコードを作成してみます。

# ALLOWED: Create the specific A record
gcloud dns record-sets create api.example.com. --zone=example-zone --type=A --rrdatas="1.2.3.4" --ttl=300 --impersonate-service-account=${SA_EMAIL}

# DENIED: Create an unauthorized name
gcloud dns record-sets create www.example.com. --zone=example-zone --type=A --rrdatas="5.6.7.8" --ttl=300 --impersonate-service-account=${SA_EMAIL}

シナリオ 2: サブドメインの委任

所要時間: 05:00

次に、p.example.com. サブドメイン内の任意のレコードを管理する権限を付与します。

IAM ポリシーを更新する

次のいずれかのオプションを選択して、ポリシーを更新します。

cat << EOF > policy_subdomain.json
{
  "bindings": [
    {
      "role": "roles/dns.admin",
      "members": ["serviceAccount:${SA_EMAIL}"],
      "condition": {
        "expression": "(resource.type == 'dns.googleapis.com/ResourceRecordSet' && resource.name.extract('/rrsets/{name}/').endsWith('.p.example.com.')) || (resource.type != 'dns.googleapis.com/ResourceRecordSet')",
        "title": "Subdomain Delegation (Standard Role)"
      }
    }
  ],
  "version": 3
}
EOF

gcloud dns managed-zones set-iam-policy example-zone --policy-file=policy_subdomain.json

オプション 2: カスタムロール アプローチ

cat << EOF > policy_subdomain_custom.json
{
  "bindings": [
    {
      "role": "projects/$(gcloud config get-value project)/roles/DnsRecordSetAdmin",
      "members": ["serviceAccount:${SA_EMAIL}"],
      "condition": {
        "expression": "resource.type == 'dns.googleapis.com/ResourceRecordSet' && resource.name.extract('/rrsets/{name}/').endsWith('.p.example.com.')",
        "title": "Subdomain Delegation (Custom Roles)"
      }
    },
    {
      "role": "projects/$(gcloud config get-value project)/roles/DnsNonRecordSetAdmin",
      "members": ["serviceAccount:${SA_EMAIL}"]
    }
  ],
  "version": 3
}
EOF

gcloud dns managed-zones set-iam-policy example-zone --policy-file=policy_subdomain_custom.json

委任を確認する

# ALLOWED: Create any record in the subdomain
gcloud dns record-sets create test.p.example.com. --zone=example-zone --type=A --rrdatas="192.168.1.1" --ttl=300 --impersonate-service-account=${SA_EMAIL}

# DENIED: Create a record outside the subdomain
gcloud dns record-sets create news.example.com. --zone=example-zone --type=A --rrdatas="192.168.1.2" --ttl=300 --impersonate-service-account=${SA_EMAIL}

シナリオ 3: バッチ変更とトランザクション

所要時間: 03:00

条件付き権限を使用する場合、トランザクションに関する重要な詳細があります。デフォルトでは、DNS トランザクションは SOA レコードの更新を試みます。IAM 条件で特定のレコード(api.example.com. など)の管理のみが許可されている場合、ユーザーは SOA レコードを変更する権限がないため、トランザクションは失敗します。

--skip-soa-update フラグ

トランザクション内で許可されたレコードを変更するには、条件を適宜変更して(resource.name.endsWith('/SOA'))、SOA の更新を許可するか、--skip-soa-update フラグを使用する必要があります。

gcloud dns record-sets transaction start --zone=example-zone --skip-soa-update
gcloud dns record-sets transaction add --zone=example-zone --name="api.example.com." --type=A --ttl=300 "10.0.0.1"
gcloud dns record-sets transaction execute --zone=example-zone --impersonate-service-account=${SA_EMAIL}

注: トランザクションに 1 つでも承認されていないレコードの変更が含まれている場合、トランザクション全体が拒否されます。

クリーンアップ

所要時間: 01:00

課金が発生しないように、このラボで作成したリソースを削除します。

# Delete record sets
gcloud dns record-sets delete api.example.com. --zone=example-zone --type=A
gcloud dns record-sets delete test.p.example.com. --zone=example-zone --type=A

# Delete managed zone
gcloud dns managed-zones delete example-zone

# Delete custom roles (only if you created them in Option 2)
gcloud iam roles delete DnsRecordSetAdmin --project=$(gcloud config get-value project) || true
gcloud iam roles delete DnsNonRecordSetAdmin --project=$(gcloud config get-value project) || true

# Delete service account
gcloud iam service-accounts delete ${SA_EMAIL}

完了

おめでとうございます!これで、Cloud DNS でレコード セットごとのきめ細かい IAM 権限を実装する方法を学習しました。

学習した内容のまとめ

  • カスタムロールを作成して、レコード セットの権限とゾーンレベルの管理タスクを分離しました。
  • 特定のレコード名とタイプに対して完全一致条件を実装しました。
  • IAM 条件で文字列抽出を使用して サブドメイン委任を実装しました。
  • --skip-soa-update フラグを使用して、条件付きユーザーが一括変更を実行できるようにしました。

参考資料