Fine-grained Cloud DNS IAM via Service Directory
Service Directory is a relatively recent addition to the GCP offering currently in beta, that offers a managed solution for discovering, publishing, and connecting services and their endpoints. This short article and example show how to leverage its Cloud DNS integration, to address the common ask of supporting fine-grained IAM control of DNS zones and records.
Before we dive into the example, let’s take a quick look at the Service Directory primitives:
- Namespaces are collections of services sharing common properties (environment, team, etc.), they are defined in a project as regional resources, and can be queried from anywhere.
- Services are the actual service representations defined in a namespace, identified by a name (unique in the namespace) and a collection of endpoints.
- Endpoints represent the individual endpoints that handle requests for a given service, identified by a name (eg vm1) and an associated unique IP/port pair.
Both Services and Endpoints can have optional metadata attached, that is returned in client queries. IAM bindings are supported at both the namespace and service level, and lookup of services and endpoints is done via HTTP, gRPC, or DNS via a dedicated private zone type.
The Cloud DNS Service Directory private zone type uses a Service Directory namespace as its authoritative source of information, allowing users to lookup Service Directory records via DNS requests, and administrators to implement fine-grained control on DNS zones and records by leveraging Service Directory IAM support.
Let’s see how this works in practice, using a simple example that creates a Service Directory namespace, ties it to a private zone, and grants the roles/servicedirectory.editor
role to two separate service accounts to allow editing of namespace or service records, as shown in this diagram.
To run the example, start by cloning the repository and changing to the example folder. If you prefer using Cloud Shell, this link will run the Git clone for you, and switch to the correct folder so that you can skip the initial step below.
git clone https://github.com/terraform-google-modules/cloud-foundation-fabric.git
cd examples/cloud-operations/dns-fine-grained-iam
Once in the right folder, run Terraform and just provide the project id where the required resources will be created, in Cloud Shell the$GOOGLE_PROJECT_ID
variable is set to the current project in the Cloud console, if you’re not in Cloud Shell or want to use a different project, replace it with the actual project id.
terraform init
# answer 'yes' when prompted after running apply
terraform apply -var project_id=$GOOGLE_PROJECT_ID
We’re now ready to test the example: we will log into the two VMs in turn, to query the Service Directory namespace via DNS using the dig
command, then manipulate its services and endpoints using the credentials of the service accounts associated with each VM.
The Terraform outputs generate preset gcloud compute ssh
commands that you can copy and run in the console to connect to each VM. Remember to adapt the testing commands below if you changed the default values for the name
, region
, or zone_domain
Terraform variables.
Connect via SSH to the ns
VM and let’s start by querying the Service Directory namespace via DNS, to verify everything works.
gcloud compute ssh dns-sd-test-ns-1 \
--zone europe-west1-b --tunnel-through-iap
dig app1.svc.example.org +short
# 127.0.0.2
# 127.0.0.3
# 127.0.0.7
dig app2.svc.example.org +short
# 127.0.0.4
# 127.0.0.5
dig _app1._tcp.app1.svc.example.org srv +short
# 10 10 80 vm1.app1.svc.example.org.
# 10 10 80 vm2.app1.svc.example.org.
# 10 10 80 vm3.app1.svc.example.org.
The DNS answers should match the comments after each command in the above snippet. Note the special format used to query SRV
records in the last command.
If all looks good, let’s verify that the ns
VM service account has edit rights on the namespace by creating a new service, and then querying its endpoints via DNS.
gcloud beta service-directory services create app3 \
--location europe-west1 --namespace dns-sd-test
# Created service [app3].
gcloud beta service-directory endpoints create vm1 \
--service app3 \
--location europe-west1 --namespace dns-sd-test \
--address 127.0.0.6 --port 80
# Created endpoint [vm1].
dig app3.svc.example.org +short
# 127.0.0.6
What we just demonstrated is that an identity with the roles/servicedirectory.editor
role on a namespace, is able to create (and edit/delete) services and endpoints bound to it.
Let’s now see how we can limit the permissions scope to a single service and its endpoints. Log out from the ns
VM and log in to the svc
VM, then try to delete the service record we just created.
gcloud compute ssh dns-sd-test-svc-1 \
--zone europe-west1-b --tunnel-through-iapgcloud beta service-directory services delete app3 \
--location europe-west1 --namespace dns-sd-test
# Deleted service [app3].
# ERROR: (gcloud.beta.service-directory.services.delete) PERMISSION_DENIED: Permission 'servicedirectory.services.delete' denied on resource 'projects/my-project/locations/europe-west1/namespaces/dns-sd-test/services/app3'.
Ignore the deleted
message which is clearly a bug (the service is still in beta after all), and focus on the last error message: this identity has no rights to operate on services other than app1
. What it can do is operate on the single service we gave it access to.
gcloud beta service-directory endpoints create vm3 \
--service app1 \
--location europe-west1 --namespace dns-sd-test \
--address 127.0.0.7 --port 80
# Created endpoint [vm3].dig app1.svc.example.org +short
# 127.0.0.2
# 127.0.0.3
# 127.0.0.7
When you’re done testing, log out from the VM and run the following command to remove the resources created for this example.
terraform destroy -var project_id=$GOOGLE_PROJECT_ID
The setup shown here is of course minimal, a real setup would involve a combination of Service Directory and peering zones to distribute management and consumption of DNS records across several projects and networks.
Its main objective is to demonstrate the integration of the two services, and to show how combining the two allows for fine grained control of private DNS zones and records.