Repository: cloudflare/cloudflare-go
Pull request #3779 (2025-01-03 15:35:47)

feat(teams_rules): support internal dns settings
## Description

This PR adds support to creating and updating DNS resolver rules configured to forward DNS queries to the internal DNS service with the given internal DNS view identifier.

## Has your change been tested?

Automated tests were added to exercise the new code.

## Types of changes

What sort of change does your code introduce/modify?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [X] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [X] I have updated the documentation accordingly.
- [X] I have added tests to cover my changes.
- [X] All new and existing tests passed.
- [X] This change is using publicly documented in [cloudflare/api-schemas](https://github.com/cloudflare/api-schemas) 
      and relies on stable APIs.

[1]: https://help.github.com/articles/closing-issues-using-keywords/


github-actions[bot]: Oops! It looks like no changelog entry is attached to this PR. Please include a release note as described in https://github.com/cloudflare/cloudflare-go/blob/master/docs/changelog-process.md.

Example: 

~~~
```release-note:TYPE
Release note
```
~~~

If you do not require a release note to be included, please add the `workflow/skip-changelog-entry` label.

Patch:
diff --git a/.changelog/3779.txt b/.changelog/3779.txt
new file mode 100644
index 000000000..90eab0bee
--- /dev/null
+++ b/.changelog/3779.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+teams_rules: add support for `resolve_dns_internally` settings on dns_resolver rules
+```
\ No newline at end of file
diff --git a/teams_rules.go b/teams_rules.go
index c4b8e5d86..aaadb53f0 100644
--- a/teams_rules.go
+++ b/teams_rules.go
@@ -67,6 +67,8 @@ type TeamsRuleSettings struct {
 	// Resolver policy settings.
 	DnsResolverSettings *TeamsDnsResolverSettings `json:"dns_resolvers,omitempty"`
 
+	ResolveDnsInternallySettings *TeamsResolveDnsInternallySettings `json:"resolve_dns_internally,omitempty"`
+
 	NotificationSettings *TeamsNotificationSettings `json:"notification_settings"`
 	Quarantine           *TeamsQuarantine           `json:"quarantine,omitempty"`
 	ForensicCopySettings *TeamsForensicCopySettings `json:"forensic_copy,omitempty"`
@@ -150,6 +152,18 @@ type (
 		VnetID                     string `json:"vnet_id,omitempty"`
 		RouteThroughPrivateNetwork *bool  `json:"route_through_private_network,omitempty"`
 	}
+
+	TeamsResolveDnsInternallySettings struct {
+		ViewID   string                                    `json:"view_id"`
+		Fallback TeamsResolveDnsInternallyFallbackStrategy `json:"fallback"`
+	}
+
+	TeamsResolveDnsInternallyFallbackStrategy string
+)
+
+const (
+	None      TeamsResolveDnsInternallyFallbackStrategy = "none"
+	PublicDns TeamsResolveDnsInternallyFallbackStrategy = "public_dns"
 )
 
 type TeamsDlpPayloadLogSettings struct {


Test patch:
diff --git a/teams_rules_test.go b/teams_rules_test.go
index 4b9f4a34d..18186832d 100644
--- a/teams_rules_test.go
+++ b/teams_rules_test.go
@@ -106,6 +106,40 @@ func TestTeamsRules(t *testing.T) {
 					},
 					"resolve_dns_through_cloudflare": true
 				  }
+				},
+				{
+				  "id": "7559a944-3dd7-41bf-b183-360a814a8caa",
+				  "name": "rule3",
+				  "description": "internal dns",
+				  "precedence": 3000,
+				  "enabled": true,
+				  "action": "resolve",
+				  "filters": [
+					"dns_resolver"
+				  ],
+				  "created_at": "2014-01-01T05:20:00.12345Z",
+				  "updated_at": "2014-01-01T05:20:00.12345Z",
+				  "deleted_at": null,
+				  "traffic": "dns.domain == \"example.com\"",
+				  "identity": "",
+				  "version": 1,
+				  "rule_settings": {
+					"block_page_enabled": false,
+					"block_reason": "",
+					"override_ips": null,
+					"override_host": "",
+					"l4override": null,
+					"biso_admin_controls": null,
+					"add_headers": null,
+					"check_session": null,
+                    "insecure_disable_dnssec_validation": false,
+					"resolve_dns_through_cloudflare": false,
+					"dns_resolvers": null,
+					"resolve_dns_internally": {
+					  "view_id": "fake_view_id",
+					  "fallback": "public_dns"
+					}
+				  }
 				}
 			]
 		}
@@ -115,69 +149,70 @@ func TestTeamsRules(t *testing.T) {
 	createdAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
 	updatedAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
 
-	want := []TeamsRule{{
-		ID:            "7559a944-3dd7-41bf-b183-360a814a8c36",
-		Name:          "rule1",
-		Description:   "rule description",
-		Precedence:    1000,
-		Enabled:       false,
-		Action:        Isolate,
-		Filters:       []TeamsFilterType{HttpFilter},
-		Traffic:       `http.host == "example.com"`,
-		DevicePosture: "",
-		Identity:      "",
-		Version:       1,
-		RuleSettings: TeamsRuleSettings{
-			BlockPageEnabled:  false,
-			BlockReason:       "",
-			OverrideIPs:       nil,
-			OverrideHost:      "",
-			L4Override:        nil,
-			AddHeaders:        nil,
-			BISOAdminControls: nil,
-			CheckSession: &TeamsCheckSessionSettings{
-				Enforce:  true,
-				Duration: Duration{900 * time.Second},
-			},
-			InsecureDisableDNSSECValidation: false,
-			UntrustedCertSettings: &UntrustedCertSettings{
-				Action: UntrustedCertError,
-			},
-			IgnoreCNAMECategoryMatches: BoolPtr(true),
-			DnsResolverSettings: &TeamsDnsResolverSettings{
-				V4Resolvers: []TeamsDnsResolverAddressV4{
-					{
-						TeamsDnsResolverAddress{
-							IP:   "10.0.0.2",
-							Port: IntPtr(5053),
+	want := []TeamsRule{
+		{
+			ID:            "7559a944-3dd7-41bf-b183-360a814a8c36",
+			Name:          "rule1",
+			Description:   "rule description",
+			Precedence:    1000,
+			Enabled:       false,
+			Action:        Isolate,
+			Filters:       []TeamsFilterType{HttpFilter},
+			Traffic:       `http.host == "example.com"`,
+			DevicePosture: "",
+			Identity:      "",
+			Version:       1,
+			RuleSettings: TeamsRuleSettings{
+				BlockPageEnabled:  false,
+				BlockReason:       "",
+				OverrideIPs:       nil,
+				OverrideHost:      "",
+				L4Override:        nil,
+				AddHeaders:        nil,
+				BISOAdminControls: nil,
+				CheckSession: &TeamsCheckSessionSettings{
+					Enforce:  true,
+					Duration: Duration{900 * time.Second},
+				},
+				InsecureDisableDNSSECValidation: false,
+				UntrustedCertSettings: &UntrustedCertSettings{
+					Action: UntrustedCertError,
+				},
+				IgnoreCNAMECategoryMatches: BoolPtr(true),
+				DnsResolverSettings: &TeamsDnsResolverSettings{
+					V4Resolvers: []TeamsDnsResolverAddressV4{
+						{
+							TeamsDnsResolverAddress{
+								IP:   "10.0.0.2",
+								Port: IntPtr(5053),
+							},
 						},
-					},
-					{
-						TeamsDnsResolverAddress{
-							IP:                         "192.168.0.2",
-							VnetID:                     "16fd7a32-11f0-4687-a0bb-7031d241e184",
-							RouteThroughPrivateNetwork: BoolPtr(true),
+						{
+							TeamsDnsResolverAddress{
+								IP:                         "192.168.0.2",
+								VnetID:                     "16fd7a32-11f0-4687-a0bb-7031d241e184",
+								RouteThroughPrivateNetwork: BoolPtr(true),
+							},
 						},
 					},
-				},
-				V6Resolvers: []TeamsDnsResolverAddressV6{
-					{
-						TeamsDnsResolverAddress{
-							IP: "2460::1",
+					V6Resolvers: []TeamsDnsResolverAddressV6{
+						{
+							TeamsDnsResolverAddress{
+								IP: "2460::1",
+							},
 						},
 					},
 				},
+				NotificationSettings: &TeamsNotificationSettings{
+					Enabled:    BoolPtr(true),
+					Message:    "message",
+					SupportURL: "https://hello.com",
+				},
 			},
-			NotificationSettings: &TeamsNotificationSettings{
-				Enabled:    BoolPtr(true),
-				Message:    "message",
-				SupportURL: "https://hello.com",
-			},
+			CreatedAt: &createdAt,
+			UpdatedAt: &updatedAt,
+			DeletedAt: nil,
 		},
-		CreatedAt: &createdAt,
-		UpdatedAt: &updatedAt,
-		DeletedAt: nil,
-	},
 		{
 			ID:            "9ae57318-f32e-46b3-b889-48dd6dcc49af",
 			Name:          "rule2",
@@ -209,7 +244,42 @@ func TestTeamsRules(t *testing.T) {
 			CreatedAt: &createdAt,
 			UpdatedAt: &updatedAt,
 			DeletedAt: nil,
-		}}
+		},
+		{
+			ID:            "7559a944-3dd7-41bf-b183-360a814a8caa",
+			Name:          "rule3",
+			Description:   "internal dns",
+			Precedence:    3000,
+			Enabled:       true,
+			Action:        Resolve,
+			Filters:       []TeamsFilterType{DnsResolverFilter},
+			Traffic:       `dns.domain == "example.com"`,
+			Identity:      "",
+			DevicePosture: "",
+			Version:       1,
+			RuleSettings: TeamsRuleSettings{
+				BlockPageEnabled:                false,
+				BlockReason:                     "",
+				OverrideIPs:                     nil,
+				OverrideHost:                    "",
+				L4Override:                      nil,
+				AddHeaders:                      nil,
+				BISOAdminControls:               nil,
+				CheckSession:                    nil,
+				InsecureDisableDNSSECValidation: false,
+				UntrustedCertSettings:           nil,
+				ResolveDnsInternallySettings: &TeamsResolveDnsInternallySettings{
+					ViewID:   "fake_view_id",
+					Fallback: PublicDns,
+				},
+				ResolveDnsThroughCloudflare: BoolPtr(false),
+				DnsResolverSettings:         nil,
+			},
+			CreatedAt: &createdAt,
+			UpdatedAt: &updatedAt,
+			DeletedAt: nil,
+		},
+	}
 
 	mux.HandleFunc("/accounts/"+testAccountID+"/gateway/rules", handler)
 
@@ -534,7 +604,7 @@ func TestTeamsCreateL4Rule(t *testing.T) {
 	}
 }
 
-func TestTeamsCreateResolverPolicy(t *testing.T) {
+func TestTeamsCreateResolverPolicyWithResolveDnsThroughCloudlare(t *testing.T) {
 	setup()
 	defer teardown()
 
@@ -603,6 +673,81 @@ func TestTeamsCreateResolverPolicy(t *testing.T) {
 	}
 }
 
+func TestTeamsCreateResolverPolicyWithResolveDnsInternally(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"name": "resolve 4.4.4.4",
+				"description": "rule description",
+				"precedence": 1000,
+				"enabled": true,
+				"action": "resolve",
+				"filters": [
+					"dns_resolver"
+				],
+				"traffic": "any(dns.domains[*] == \"scottstots.com\")",
+				"identity": "",
+				"rule_settings": {					
+					"resolve_dns_through_cloudflare": false,
+					"dns_resolvers": null,
+					"resolve_dns_internally": {
+					  "view_id": "fake_view_id",
+					  "fallback": "none"
+					}
+				}
+			}
+		}
+		`)
+	}
+
+	want := TeamsRule{
+		Name:          "resolve 4.4.4.4",
+		Description:   "rule description",
+		Precedence:    1000,
+		Enabled:       true,
+		Action:        Resolve,
+		Filters:       []TeamsFilterType{DnsResolverFilter},
+		Traffic:       `any(dns.domains[*] == "scottstots.com")`,
+		Identity:      "",
+		DevicePosture: "",
+		RuleSettings: TeamsRuleSettings{
+			BlockPageEnabled:                false,
+			BlockReason:                     "",
+			OverrideIPs:                     nil,
+			OverrideHost:                    "",
+			L4Override:                      nil,
+			AddHeaders:                      nil,
+			BISOAdminControls:               nil,
+			CheckSession:                    nil,
+			InsecureDisableDNSSECValidation: false,
+			EgressSettings:                  nil,
+			ResolveDnsThroughCloudflare:     BoolPtr(false),
+			DnsResolverSettings:             nil,
+			ResolveDnsInternallySettings: &TeamsResolveDnsInternallySettings{
+				ViewID:   "fake_view_id",
+				Fallback: None,
+			},
+		},
+		DeletedAt: nil,
+	}
+
+	mux.HandleFunc("/accounts/"+testAccountID+"/gateway/rules", handler)
+
+	actual, err := client.TeamsCreateRule(context.Background(), testAccountID, want)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
 func TestTeamsUpdateRule(t *testing.T) {
 	setup()
 	defer teardown()