Skip to content

Commit 2a5932f

Browse files
feat: add authorization policy for spoke -> hub DNS access (#775)
1 parent 365c23b commit 2a5932f

File tree

7 files changed

+69
-2
lines changed

7 files changed

+69
-2
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This module creates the following IBM Cloud® Virtual Private Cloud (VPC) net
1414
- Network ACLs: Create network ACLs with multiple rules. By default, VPC network ACLs can have no more than 25 rules.
1515
- VPN gateways: Create VPN gateways on your subnets by using the `vpn_gateways` variable. For more information about VPN gateways on VPC, see [About site-to-site VPN gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-using-vpn) in the IBM Cloud docs.
1616
- VPN gateway connections: Add connections to a VPN gateway.
17-
- Hub and spoke DNS-sharing model: Optionally create a hub or spoke VPC, with associated custom resolver and DNS resolution binding. See [About DNS sharing for VPE gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model) in the IBM Cloud docs for details.
17+
- Hub and spoke DNS-sharing model: Optionally create a hub or spoke VPC, with associated custom resolver and DNS resolution binding, as well as a service-to-service authorization policy which supports the hub and spoke VPCs to be in separate accounts. See [About DNS sharing for VPE gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model) in the IBM Cloud docs for details.
1818

1919
![vpc-module](https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/main/.docs/vpc-module.png)
2020

@@ -117,6 +117,7 @@ To attach access management tags to resources in this module, you need the follo
117117
|------|------|
118118
| [ibm_dns_custom_resolver.custom_resolver_hub](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/dns_custom_resolver) | resource |
119119
| [ibm_iam_authorization_policy.policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource |
120+
| [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource |
120121
| [ibm_is_flow_log.flow_logs](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_flow_log) | resource |
121122
| [ibm_is_network_acl.network_acl](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_network_acl) | resource |
122123
| [ibm_is_public_gateway.gateway](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_public_gateway) | resource |
@@ -132,6 +133,7 @@ To attach access management tags to resources in this module, you need the follo
132133
| [ibm_is_vpc_routing_table_route.routing_table_routes](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_vpc_routing_table_route) | resource |
133134
| [ibm_resource_instance.dns_instance_hub](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_instance) | resource |
134135
| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
136+
| [ibm_iam_account_settings.iam_account_settings](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/iam_account_settings) | data source |
135137
| [ibm_is_subnet.subnet](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_subnet) | data source |
136138
| [ibm_is_vpc.vpc](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_vpc) | data source |
137139
| [ibm_is_vpc_address_prefixes.get_address_prefixes](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_vpc_address_prefixes) | data source |
@@ -164,6 +166,7 @@ To attach access management tags to resources in this module, you need the follo
164166
| <a name="input_existing_storage_bucket_name"></a> [existing\_storage\_bucket\_name](#input\_existing\_storage\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no |
165167
| <a name="input_existing_subnets"></a> [existing\_subnets](#input\_existing\_subnets) | The detail of the existing subnets and required mappings to other resources. Required if 'create\_subnets' is false. | <pre>list(object({<br> id = string<br> public_gateway = optional(bool, false)<br> }))</pre> | `[]` | no |
166168
| <a name="input_existing_vpc_id"></a> [existing\_vpc\_id](#input\_existing\_vpc\_id) | The ID of the existing vpc. Required if 'create\_vpc' is false. | `string` | `null` | no |
169+
| <a name="input_hub_account_id"></a> [hub\_account\_id](#input\_hub\_account\_id) | ID of the hub account for DNS resolution, required if 'skip\_spoke\_auth\_policy' is false. | `string` | `null` | no |
167170
| <a name="input_hub_vpc_crn"></a> [hub\_vpc\_crn](#input\_hub\_vpc\_crn) | Indicates the crn of the hub VPC for DNS resolution. See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model. Mutually exclusive with hub\_vpc\_id. | `string` | `null` | no |
168171
| <a name="input_hub_vpc_id"></a> [hub\_vpc\_id](#input\_hub\_vpc\_id) | Indicates the id of the hub VPC for DNS resolution. See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model. Mutually exclusive with hub\_vpc\_crn. | `string` | `null` | no |
169172
| <a name="input_is_flow_log_collector_active"></a> [is\_flow\_log\_collector\_active](#input\_is\_flow\_log\_collector\_active) | Indicates whether the collector is active. If false, this collector is created in inactive mode. | `bool` | `true` | no |
@@ -180,6 +183,7 @@ To attach access management tags to resources in this module, you need the follo
180183
| <a name="input_routing_table_name"></a> [routing\_table\_name](#input\_routing\_table\_name) | The name to give the provisioned routing tables. If not set, the module generates a name based on the `prefix` and `name` variables. | `string` | `null` | no |
181184
| <a name="input_security_group_rules"></a> [security\_group\_rules](#input\_security\_group\_rules) | A list of security group rules to be added to the default vpc security group (default empty) | <pre>list(<br> object({<br> name = string<br> direction = string<br> remote = string<br> tcp = optional(<br> object({<br> port_max = optional(number)<br> port_min = optional(number)<br> })<br> )<br> udp = optional(<br> object({<br> port_max = optional(number)<br> port_min = optional(number)<br> })<br> )<br> icmp = optional(<br> object({<br> type = optional(number)<br> code = optional(number)<br> })<br> )<br> })<br> )</pre> | `[]` | no |
182185
| <a name="input_skip_custom_resolver_hub_creation"></a> [skip\_custom\_resolver\_hub\_creation](#input\_skip\_custom\_resolver\_hub\_creation) | Indicates whether to skip the configuration of a custom resolver in the hub VPC. Only relevant if enable\_hub is set to true. | `bool` | `false` | no |
186+
| <a name="input_skip_spoke_auth_policy"></a> [skip\_spoke\_auth\_policy](#input\_skip\_spoke\_auth\_policy) | Set to true to skip the creation of an authorization policy between the DNS resolution spoke and hub, only enable this if a policy already exists between these two VPCs. See https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=ui for more details. | `bool` | `false` | no |
183187
| <a name="input_subnets"></a> [subnets](#input\_subnets) | List of subnets for the vpc. For each item in each array, a subnet will be created. Items can be either CIDR blocks or total ipv4 addressess. Public gateways will be enabled only in zones where a gateway has been created | <pre>object({<br> zone-1 = list(object({<br> name = string<br> cidr = string<br> public_gateway = optional(bool)<br> acl_name = string<br> no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true<br> }))<br> zone-2 = optional(list(object({<br> name = string<br> cidr = string<br> public_gateway = optional(bool)<br> acl_name = string<br> no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true<br> })))<br> zone-3 = optional(list(object({<br> name = string<br> cidr = string<br> public_gateway = optional(bool)<br> acl_name = string<br> no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true<br> })))<br> })</pre> | <pre>{<br> "zone-1": [<br> {<br> "acl_name": "vpc-acl",<br> "cidr": "10.10.10.0/24",<br> "name": "subnet-a",<br> "no_addr_prefix": false,<br> "public_gateway": true<br> }<br> ],<br> "zone-2": [<br> {<br> "acl_name": "vpc-acl",<br> "cidr": "10.20.10.0/24",<br> "name": "subnet-b",<br> "no_addr_prefix": false,<br> "public_gateway": true<br> }<br> ],<br> "zone-3": [<br> {<br> "acl_name": "vpc-acl",<br> "cidr": "10.30.10.0/24",<br> "name": "subnet-c",<br> "no_addr_prefix": false,<br> "public_gateway": false<br> }<br> ]<br>}</pre> | no |
184188
| <a name="input_tags"></a> [tags](#input\_tags) | List of Tags for the resource created | `list(string)` | `null` | no |
185189
| <a name="input_update_delegated_resolver"></a> [update\_delegated\_resolver](#input\_update\_delegated\_resolver) | If set to true, and if the vpc is configured to be a spoke for DNS resolution (enable\_hub\_vpc\_crn or enable\_hub\_vpc\_id set), then the spoke VPC resolver will be updated to a delegated resolver. | `bool` | `false` | no |

examples/existing_vpc/variables.tf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,4 @@ variable "existing_resource_group_name" {
3232
variable "name" {
3333
description = "The string is used as a prefix for the naming of VPC resources."
3434
type = string
35-
default = null
3635
}

examples/hub-spoke-delegated-resolver/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ This example demonstrates how to deploy hub and spoke VPCs, inclusive of enablin
44
- The 2 VPCs are connected through a transit gateway.
55
- The hub VPC is configured with a custom resolver.
66
- The spoke VPC is configured with a delegated DNS resolver. DNS requests are resolved by the hub VPC.
7+
- An authorization policy for the DNS Binding Connector role is created to allow the spoke VPC to use the DNS resolution of the hub VPC, this also allows the hub and spoke VPCs to be in separate accounts.
78
- A DNS resolution binding relationship is configured to enable the hub VPC to DNS resolve VPE in the spoke VPC.
89

910

examples/hub-spoke-delegated-resolver/main.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,16 @@ module "hub_vpc" {
5151
}
5252

5353

54+
data "ibm_iam_account_settings" "iam_account_settings" {}
55+
5456
module "spoke_vpc" {
5557
source = "../../"
5658
resource_group_id = module.resource_group.resource_group_id
5759
region = var.region
5860
name = "spoke"
5961
prefix = "${var.prefix}-spoke"
6062
tags = var.resource_tags
63+
hub_account_id = data.ibm_iam_account_settings.iam_account_settings.account_id
6164
hub_vpc_crn = module.hub_vpc.vpc_crn
6265
enable_hub_vpc_crn = true
6366
update_delegated_resolver = var.update_delegated_resolver

examples/hub-spoke-manual-resolver/main.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ module "hub_vpc" {
5050
}
5151
}
5252

53+
data "ibm_iam_account_settings" "iam_account_settings" {}
5354

5455
module "spoke_vpc" {
5556
source = "../../"
@@ -58,6 +59,7 @@ module "spoke_vpc" {
5859
name = "spoke"
5960
prefix = "${var.prefix}-spoke"
6061
tags = var.resource_tags
62+
hub_account_id = data.ibm_iam_account_settings.iam_account_settings.account_id
6163
hub_vpc_crn = module.hub_vpc.vpc_crn
6264
enable_hub_vpc_crn = true
6365
update_delegated_resolver = false

main.tf

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ locals {
3737

3838
# tflint-ignore: terraform_unused_declarations
3939
validate_vpc_flow_logs_inputs = (var.enable_vpc_flow_logs) ? ((var.create_authorization_policy_vpc_to_cos) ? ((var.existing_cos_instance_guid != null && var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS instance & bucket name to create flow logs collector.")) : ((var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS bucket name to create flow logs collector"))) : false
40+
41+
# tflint-ignore: terraform_unused_declarations
42+
validate_skip_spoke_auth_policy_input = (var.hub_account_id == null && !var.skip_spoke_auth_policy && !var.enable_hub && (var.enable_hub_vpc_id || var.enable_hub_vpc_crn)) ? tobool("var.hub_account_id must be set when var.skip_spoke_auth_policy is False and either var.enable_hub_vpc_id or var.enable_hub_vpc_crn is true.") : true
4043
}
4144

4245
##############################################################################
@@ -116,9 +119,50 @@ resource "ibm_is_vpc" "vpc" {
116119
# See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model for context
117120
##############################################################################
118121

122+
# fetch this account ID
123+
data "ibm_iam_account_settings" "iam_account_settings" {}
124+
125+
# spoke -> hub auth policy based on https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=terraform
126+
resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" {
127+
count = (var.enable_hub == false && var.skip_spoke_auth_policy == false && (var.enable_hub_vpc_id || var.enable_hub_vpc_crn)) ? 1 : 0
128+
roles = ["DNS Binding Connector"]
129+
# subject is the spoke
130+
subject_attributes {
131+
name = "accountId"
132+
value = data.ibm_iam_account_settings.iam_account_settings.account_id
133+
}
134+
subject_attributes {
135+
name = "serviceName"
136+
value = "is"
137+
}
138+
subject_attributes {
139+
name = "resourceType"
140+
value = "vpc"
141+
}
142+
subject_attributes {
143+
name = "resource"
144+
value = local.vpc_id
145+
}
146+
# resource is the hub
147+
resource_attributes {
148+
name = "accountId"
149+
value = var.hub_account_id
150+
}
151+
resource_attributes {
152+
name = "serviceName"
153+
value = "is"
154+
}
155+
resource_attributes {
156+
name = "vpcId"
157+
value = var.enable_hub_vpc_id ? var.hub_vpc_id : split(":", var.hub_vpc_crn)[9]
158+
}
159+
}
160+
119161
# Enable Hub to dns resolve in spoke VPC
120162
resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" {
121163
count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0
164+
# Depends on required as the authorization policy cannot be directly referenced
165+
depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy]
122166

123167
# Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination.
124168
name = coalesce(
@@ -133,6 +177,8 @@ resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" {
133177

134178
resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_crn" {
135179
count = (var.enable_hub == false && var.enable_hub_vpc_crn) ? 1 : 0
180+
# Depends on required as the authorization policy cannot be directly referenced
181+
depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy]
136182

137183
# Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination.
138184
name = coalesce(

variables.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,18 @@ variable "enable_hub" {
542542
default = false
543543
}
544544

545+
variable "skip_spoke_auth_policy" {
546+
description = "Set to true to skip the creation of an authorization policy between the DNS resolution spoke and hub, only enable this if a policy already exists between these two VPCs. See https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=ui for more details."
547+
type = bool
548+
default = false
549+
}
550+
551+
variable "hub_account_id" {
552+
description = "ID of the hub account for DNS resolution, required if 'skip_spoke_auth_policy' is false."
553+
type = string
554+
default = null
555+
}
556+
545557
variable "enable_hub_vpc_id" {
546558
description = "Indicates whether Hub VPC ID is passed."
547559
type = bool

0 commit comments

Comments
 (0)