Service directory DNS example (#120)
* dns service directory example * fix module references in asset feeds example * update diagram * update diagram * commands * link Medium article in asset inventory examples' README * finish service directory dns example
This commit is contained in:
parent
482f4464f8
commit
d8b8a5396d
|
@ -9,6 +9,8 @@ The Cloud Function can then be used for different purposes:
|
|||
- adapting the configuration of separate related resources
|
||||
- implementing remediation steps that enforce policy compliance by tweaking or reverting the changes.
|
||||
|
||||
A [companion Medium article](https://medium.com/google-cloud/using-cloud-asset-inventory-feeds-for-dynamic-configuration-and-policy-enforcement-c37b6a590c49) has been published for this example, refer to it for more details on the context and the specifics of running the example.
|
||||
|
||||
This example shows a simple remediation use case: how to enforce policies on instance tags and revert non-compliant changes in near-real time, thus adding an additional measure of control when using tags for firewall rule scoping. Changing the [monitored asset](https://cloud.google.com/asset-inventory/docs/supported-asset-types) and the function logic allows simple adaptation to other common use cases:
|
||||
|
||||
- enforcing a centrally defined Cloud Armor policy in backend services
|
||||
|
|
|
@ -20,7 +20,7 @@ locals {
|
|||
}
|
||||
|
||||
module "project" {
|
||||
source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v2.3.0"
|
||||
source = "../../modules/project"
|
||||
name = var.project_id
|
||||
project_create = false
|
||||
services = [
|
||||
|
@ -43,7 +43,7 @@ module "project" {
|
|||
}
|
||||
|
||||
module "vpc" {
|
||||
source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpc?ref=v2.3.0"
|
||||
source = "../../modules/net-vpc"
|
||||
project_id = module.project.project_id
|
||||
name = var.name
|
||||
subnets = [{
|
||||
|
@ -55,7 +55,7 @@ module "vpc" {
|
|||
}
|
||||
|
||||
module "pubsub" {
|
||||
source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/pubsub?ref=v2.3.0"
|
||||
source = "../../modules/pubsub"
|
||||
project_id = module.project.project_id
|
||||
name = var.name
|
||||
subscriptions = { "${var.name}-default" = null }
|
||||
|
@ -70,14 +70,14 @@ module "pubsub" {
|
|||
}
|
||||
|
||||
module "service-account" {
|
||||
source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-accounts?ref=v2.3.0"
|
||||
source = "../../modules/iam-service-accounts"
|
||||
project_id = module.project.project_id
|
||||
names = ["${var.name}-cf"]
|
||||
iam_project_roles = { (module.project.project_id) = [local.role_id] }
|
||||
}
|
||||
|
||||
module "cf" {
|
||||
source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/cloud-function?ref=v2.3.0"
|
||||
source = "../../modules/cloud-function"
|
||||
project_id = module.project.project_id
|
||||
name = var.name
|
||||
bucket_name = "${var.name}-${random_pet.random.id}"
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
# Fine-grained Cloud DNS IAM via Service Directory
|
||||
|
||||
This example shows how to leverage [Service Directory](https://cloud.google.com/blog/products/networking/introducing-service-directory) and Cloud DNS Service Directory private zones, to implement fine-grained IAM controls on DNS.
|
||||
|
||||
<!-- A [companion Medium article](https://medium.com/google-cloud/using-cloud-asset-inventory-feeds-for-dynamic-configuration-and-policy-enforcement-c37b6a590c49) has been published for this example, refer to it for more details on the context and the specifics of running the example. -->
|
||||
|
||||
This example:
|
||||
|
||||
- creates a Service Directory namespace with two services and their endpoints
|
||||
- creates a Cloud DNS private zone that uses the namespace as its authoritative source
|
||||
- creates two service accounts and assigns them the `roles/servicedirectory.editor` role on the namespace and on one service respectively
|
||||
- creates two VMs and sets them to use the two service accounts, so that DNS queries and `gcloud` commands can be used to verify the setup
|
||||
|
||||
The resources created in this example are shown in the high level diagram below:
|
||||
|
||||
<img src="diagram.png" width="640px">
|
||||
|
||||
|
||||
## Running the example
|
||||
|
||||
Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=cloud-operations%2Fcloud-operations/dns-fine-grained-iam), then go through the following steps to create resources:
|
||||
|
||||
- `terraform init`
|
||||
- `terraform apply -var project_id=my-project-id`
|
||||
|
||||
Once done testing, you can clean up resources by running `terraform destroy`. To persist state, check out the `backend.tf.sample` file.
|
||||
|
||||
## Testing the example
|
||||
|
||||
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` variables.
|
||||
|
||||
Connect via SSH to the `ns` VM and query the Service Directory namespace via DNS.
|
||||
|
||||
```bash
|
||||
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 ones in the comments above, after each command. Note the special format used to query `SRV` records.
|
||||
|
||||
If the above looks good, let's verify that the `ns` VM service account has edit rights on the namespace by creating a new service, and then verifying it via DNS.
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
Log out from the `ns` VM and log in to the `svc` VM, then verify that its service account has no permissions on the whole namespace.
|
||||
|
||||
```bash
|
||||
gcloud compute ssh dns-sd-test-svc-1 \
|
||||
--zone europe-west1-b \
|
||||
--tunnel-through-iap
|
||||
|
||||
gcloud 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'.
|
||||
```
|
||||
|
||||
Ignoring the `deleted` message which is clearly a bug (the service is still in beta after all), the error message shows that this identity has no rights to operate on the namespace. What it can do is operate on the single service we gave it access to.
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---: |:---:|:---:|
|
||||
| project_id | Existing project id. | <code title="">string</code> | ✓ | |
|
||||
| *name* | Arbitrary string used to name created resources. | <code title="">string</code> | | <code title="">dns-sd-test</code> |
|
||||
| *region* | Compute region used in the example. | <code title="">string</code> | | <code title="">europe-west1</code> |
|
||||
| *zone_domain* | Domain name used for the DNS zone. | <code title="">string</code> | | <code title="">svc.example.org.</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| gcloud_commands | Commands used to SSH to the VMs. | |
|
||||
| vms | VM names. | |
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright 2019 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# set a valid bucket below and rename this file to backend.tf
|
||||
|
||||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = ""
|
||||
prefix = "fabric/operations/service-directory-dns"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
################################# Quickstart #################################
|
||||
|
||||
- terraform init
|
||||
- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT
|
||||
|
||||
Refer to the README.md file for more info and testing flow.
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
startup-script = <<END
|
||||
#!/bin/bash
|
||||
apt install -y bash-completion dnsutils
|
||||
END
|
||||
}
|
||||
|
||||
module "project" {
|
||||
source = "../../modules/project"
|
||||
name = var.project_id
|
||||
project_create = false
|
||||
services = [
|
||||
"compute.googleapis.com",
|
||||
"dns.googleapis.com",
|
||||
"servicedirectory.googleapis.com"
|
||||
]
|
||||
service_config = {
|
||||
disable_on_destroy = false, disable_dependent_services = false
|
||||
}
|
||||
}
|
||||
|
||||
module "vpc" {
|
||||
source = "../../modules/net-vpc"
|
||||
project_id = module.project.project_id
|
||||
name = var.name
|
||||
subnets = [{
|
||||
ip_cidr_range = "192.168.0.0/24"
|
||||
name = "${var.name}-default"
|
||||
region = var.region
|
||||
secondary_ip_range = {}
|
||||
}]
|
||||
}
|
||||
|
||||
module "firewall-a" {
|
||||
source = "../../modules/net-vpc-firewall"
|
||||
project_id = module.project.project_id
|
||||
network = module.vpc.name
|
||||
}
|
||||
|
||||
module "nat-a" {
|
||||
source = "../../modules/net-cloudnat"
|
||||
project_id = module.project.project_id
|
||||
region = var.region
|
||||
name = var.name
|
||||
router_network = module.vpc.name
|
||||
}
|
||||
|
||||
module "dns-service-zone" {
|
||||
source = "../../modules/dns"
|
||||
project_id = module.project.project_id
|
||||
type = "service-directory"
|
||||
name = var.name
|
||||
domain = var.zone_domain
|
||||
client_networks = [module.vpc.self_link]
|
||||
service_directory_namespace = module.service-directory.id
|
||||
}
|
||||
|
||||
module "service-directory" {
|
||||
source = "../../modules/service-directory"
|
||||
project_id = module.project.project_id
|
||||
location = var.region
|
||||
name = var.name
|
||||
iam_members = {
|
||||
"roles/servicedirectory.editor" = [
|
||||
module.vm-ns-editor.service_account_iam_email
|
||||
]
|
||||
}
|
||||
iam_roles = ["roles/servicedirectory.editor"]
|
||||
services = {
|
||||
app1 = { endpoints = ["vm1", "vm2"], metadata = null }
|
||||
app2 = { endpoints = ["vm1", "vm2"], metadata = null }
|
||||
}
|
||||
service_iam_members = {
|
||||
app1 = {
|
||||
"roles/servicedirectory.editor" = [
|
||||
module.vm-svc-editor.service_account_iam_email
|
||||
]
|
||||
}
|
||||
}
|
||||
service_iam_roles = {
|
||||
app1 = ["roles/servicedirectory.editor"]
|
||||
}
|
||||
endpoint_config = {
|
||||
"app1/vm1" = { address = "127.0.0.2", port = 80, metadata = {} }
|
||||
"app1/vm2" = { address = "127.0.0.3", port = 80, metadata = {} }
|
||||
"app2/vm1" = { address = "127.0.0.4", port = 80, metadata = {} }
|
||||
"app2/vm2" = { address = "127.0.0.5", port = 80, metadata = {} }
|
||||
}
|
||||
}
|
||||
|
||||
module "vm-ns-editor" {
|
||||
source = "../../modules/compute-vm"
|
||||
project_id = module.project.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-ns"
|
||||
network_interfaces = [{
|
||||
network = module.vpc.self_link,
|
||||
subnetwork = module.vpc.subnet_self_links["${var.region}/${var.name}-default"],
|
||||
nat = false,
|
||||
addresses = null
|
||||
}]
|
||||
metadata = { startup-script = local.startup-script }
|
||||
service_account_create = true
|
||||
tags = ["ssh"]
|
||||
instance_count = 1
|
||||
}
|
||||
|
||||
module "vm-svc-editor" {
|
||||
source = "../../modules/compute-vm"
|
||||
project_id = module.project.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-svc"
|
||||
network_interfaces = [{
|
||||
network = module.vpc.self_link,
|
||||
subnetwork = module.vpc.subnet_self_links["${var.region}/${var.name}-default"],
|
||||
nat = false,
|
||||
addresses = null
|
||||
}]
|
||||
metadata = { startup-script = local.startup-script }
|
||||
service_account_create = true
|
||||
tags = ["ssh"]
|
||||
instance_count = 1
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
output "vms" {
|
||||
description = "VM names."
|
||||
value = {
|
||||
ns-editor = module.vm-ns-editor.names.0
|
||||
svc-editor = module.vm-svc-editor.names.0
|
||||
}
|
||||
}
|
||||
|
||||
output "gcloud_commands" {
|
||||
description = "Commands used to SSH to the VMs."
|
||||
value = {
|
||||
ns-editor = "gcloud compute ssh ${module.vm-ns-editor.names.0} --zone ${var.region}-b --tunnel-through-iap"
|
||||
svc-editor = "gcloud compute ssh ${module.vm-svc-editor.names.0} --zone ${var.region}-b --tunnel-through-iap"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "name" {
|
||||
description = "Arbitrary string used to name created resources."
|
||||
type = string
|
||||
default = "dns-sd-test"
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Existing project id."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "Compute region used in the example."
|
||||
type = string
|
||||
default = "europe-west1"
|
||||
}
|
||||
|
||||
variable "zone_domain" {
|
||||
description = "Domain name used for the DNS zone."
|
||||
type = string
|
||||
default = "svc.example.org."
|
||||
}
|
Loading…
Reference in New Issue