Skip to content

provider/google: use Get instead of List when single zone ID is confi…#6408

Open
knoblichd wants to merge 6 commits into
kubernetes-sigs:masterfrom
knoblichd:plt-4357-skip-list-when-single-zone-id
Open

provider/google: use Get instead of List when single zone ID is confi…#6408
knoblichd wants to merge 6 commits into
kubernetes-sigs:masterfrom
knoblichd:plt-4357-skip-list-when-single-zone-id

Conversation

@knoblichd
Copy link
Copy Markdown

When exactly one zone ID filter is configured, Zones() unconditionally calls dns.ManagedZones.List even though the zone is already known. This requires dns.managedZones.list — a project-level IAM permission that cannot be scoped to a single zone — exposing all zone names in the GCP project to any entity with that binding.

What does it do?

Adds a fast-path in Zones(): when zoneIDFilter contains exactly one ID, call dns.ManagedZones.Get directly instead of dns.ManagedZones.List. This eliminates the need for the dns.managedZones.list permission entirely, fully scoping the service account to a single zone.

Motivation

In multi-tenant GCP environments where multiple external-dns instances share a project, the list permission allows a compromised SA to enumerate all zone names project-wide, enabling cross-environment reconnaissance. With this change, each SA only needs dns.managedZones.get scoped to its own zone — it has no visibility into any other zone in the project.

More

  • Yes, this PR title follows Conventional Commits
  • Yes, I added unit tests
  • Yes, I updated end user documentation accordingly

@k8s-ci-robot k8s-ci-robot requested review from szuecs and vflaux May 1, 2026 19:09
@k8s-ci-robot k8s-ci-robot added the provider Issues or PRs related to a provider label May 1, 2026
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented May 1, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

Welcome @knoblichd!

It looks like this is your first PR to kubernetes-sigs/external-dns 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/external-dns has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label May 1, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

Hi @knoblichd. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels May 1, 2026
@ivankatliarchuk
Copy link
Copy Markdown
Member

CLA no signed

@ivankatliarchuk
Copy link
Copy Markdown
Member

Not too shure about implementation. Why only for single id, but not for all ids, otherwise the solution is very specific for a single use case. Project may have multiple zones, while we only would like to provide access only to specific zones to external-dns, not specifically one

@knoblichd
Copy link
Copy Markdown
Author

Not too shure about implementation. Why only for single id, but not for all ids, otherwise the solution is very specific for a single use case. Project may have multiple zones, while we only would like to provide access only to specific zones to external-dns, not specifically one

Good point — updated to use Get for all configured zone IDs, not just when a single ID is set. When --zone-id-filter is configured with any number of IDs, we now iterate through them calling Get on each and aggregate the results.

When no zone ID filter is configured, we fall back to List as before — in that case the operator has intentionally chosen to let external-dns discover and manage all zones in the project, so the project-level permission is unavoidable and expected.

This way the permission footprint is proportional to what the operator configured: scoped deployments no longer require project-level list visibility, while unfiltered deployments continue to work unchanged.

@knoblichd
Copy link
Copy Markdown
Author

Working on CLA. Running down who at our Org will handle that aspect.

@ivankatliarchuk
Copy link
Copy Markdown
Member

I looked at the proposed solution. Looks like there is a problem

Example. The added concern - split-horizon zones: If you have a public and private zone with the same DNS name (e.g., "example.com."), they have distinct resource names in GCP (e.g., "my-zone-public", "my-zone-private"). In the old code, List() returns both, then the visibility filter keeps only the private one. In the new code, you'd need to have the exact private zone name in your filter - a suffix like "my-zone" that previously matched both and let the visibility filter do the disambiguation now just 404s on Get.

Basically the problem, there is no way to find out where we passed a zone suffix or full zone id.

@knoblichd
Copy link
Copy Markdown
Author

knoblichd commented May 4, 2026

I looked at the proposed solution. Looks like there is a problem

Example. The added concern - split-horizon zones: If you have a public and private zone with the same DNS name (e.g., "example.com."), they have distinct resource names in GCP (e.g., "my-zone-public", "my-zone-private"). In the old code, List() returns both, then the visibility filter keeps only the private one. In the new code, you'd need to have the exact private zone name in your filter - a suffix like "my-zone" that previously matched both and let the visibility filter do the disambiguation now just 404s on Get.

Basically the problem, there is no way to find out where we passed a zone suffix or full zone id.

Good catch on split-horizon. Updated to handle it: we attempt Get for each zone ID, but if we get a 404 (indicating the ID may be a suffix pattern rather than an exact name), we fall back to List so the existing suffix-match + visibility-filter behavior is fully preserved. Exact zone IDs still benefit from the reduced IAM permissions — suffix patterns degrade gracefully to the previous behavior with no change for existing deployments.

EDIT: Why

To add context on the motivation: in multi-tenant environments where multiple external-dns instances share a single GCP project, each instance's service account currently requires dns.managedZones.list — a project-level permission. This means any compromised SA can enumerate all zone names across every tenant in the project, enabling cross-environment reconnaissance. By using Get when exact zone IDs are configured, each SA only needs access to its own zone with zero project-level visibility into other tenants.

@knoblichd knoblichd force-pushed the plt-4357-skip-list-when-single-zone-id branch from fb3a4b9 to ff778b8 Compare May 4, 2026 16:28
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels May 4, 2026
@ivankatliarchuk
Copy link
Copy Markdown
Member

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels May 5, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented May 5, 2026

Coverage Report for CI Build 25680782779

Coverage decreased (-0.006%) to 80.582%

Details

  • Coverage decreased (-0.006%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 43 coverage regressions across 2 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

43 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
google/google.go 42 76.41%
openshift_route.go 1 82.93%

Coverage Stats

Coverage Status
Relevant Lines: 21418
Covered Lines: 17259
Line Coverage: 80.58%
Coverage Strength: 1450.01 hits per line

💛 - Coveralls

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels May 5, 2026
knoblichd added 6 commits May 11, 2026 10:43
…gured

When exactly one zone ID filter is set, call dns.ManagedZones.Get directly
instead of dns.ManagedZones.List. This eliminates the need for the
dns.managedZones.list IAM permission, which is a project-level operation
that exposes all zones in the project — enabling cross-environment
enumeration when multiple tenants share a GCP project.
Extend the Get-instead-of-List optimization to all deployments where
zoneIDFilter is configured, not just single-zone. When zone ID filters
are set, call dns.ManagedZones.Get for each configured ID and aggregate
results, eliminating the need for dns.managedZones.list entirely.

This addresses multi-zone deployments where operators want external-dns
to manage a specific subset of zones without granting project-level list
visibility across all zones in the project.
When a zone ID filter doesn't resolve via Get (e.g. a suffix pattern like
"my-zone" used in split-horizon setups where both "my-zone-public" and
"my-zone-private" exist), fall back to List so the existing suffix-match
and visibility-filter behavior is preserved.

Exact zone IDs still benefit from the reduced IAM permissions (no
dns.managedZones.list required). Suffix patterns degrade gracefully to
the previous List-based behavior.
@knoblichd knoblichd force-pushed the plt-4357-skip-list-when-single-zone-id branch from f74b90f to 1453d0d Compare May 11, 2026 15:46
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign vflaux for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. provider Issues or PRs related to a provider size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants