レコードセットに対する 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

IAM 条件を標準の roles/dns.admin ロールに直接バインドすることはできません。代わりに、次の 2 つのカスタムロールを使用して、レコード セットの管理を他の管理タスクから分離する必要があります。

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

これらのロールを分割することで、Cloud DNS によって実行される前提条件チェック(dns.changes.create など)が無条件で満たされ、実際のレコード変更は条件によって厳密に制御されます。

カスタムロールの作成

Duration: 05:00

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

権限セットを定義する

# 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: 完全一致

Duration: 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 ポリシーを適用する

DnsNonRecordSetAdmin を無条件で付与し、DnsRecordSetAdmin を条件付きで付与します。

cat << EOF > policy.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"
      }
    },
    {
      "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.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: サブドメインの委任

Duration: 05:00

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

IAM ポリシーを更新する

サブドメイン サフィックスを照合するために resource.name.extract() を使用するように条件を変更します。

cat << EOF > policy_subdomain.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"
      }
    },
    {
      "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.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
gcloud iam roles delete DnsRecordSetAdmin --project=$(gcloud config get-value project)
gcloud iam roles delete DnsNonRecordSetAdmin --project=$(gcloud config get-value project)

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

完了

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

学習した内容のまとめ

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

参考資料