---
title: "Secure egress access with DNS Policy and NetworkSets"
source: "https://www.tigera.io/blog/secure-egress-access-with-dns-policy-and-networksets/"
---

[Technical Blog](https://www.tigera.io/category/technical-blog/)

# Secure egress access with DNS Policy and NetworkSets

By [Regis Martins](https://www.tigera.io/blog/author/regis-martins/) on Jun 15, 2023 • 6 min read

One of the common concerns about migrating applications to Kubernetes is the control over the network traffic [egressing](https://docs.tigera.io/calico/latest/about/kubernetes-training/about-kubernetes-egress) your environment. Due to its highly dynamic architecture, Kubernetes will not tie a specific IP address to an application by default. Instead, it will lease a different IP address from an IP pool whenever the application restarts.

Suppose you use traditional network security appliances like firewalls to provide network perimeter protection. In this case, enforcing the least privilege principle by allowing only a specific application to egress traffic outside your network is impossible. In a firewall configuration, you must specify the application IP address when creating the egress rule. As it is impossible to determine the application IP address, you must allow access to the nodes’ IP addresses if you are using overlay, or to the entire IP range you use in the IP pool. By doing this, you will also allow access to any application running on that Kubernetes cluster.

![Diagram of application traffic from a Kubernetes node going through a firewall to a 3rd party API over the internet](https://www.tigera.io/app/uploads/2023/06/Secure-egress-access-with-DNS-Policy-and-NetworkSets-1.png)

***Learn more: [Kubernetes Network Policy: Code Example and Best Practices](https://www.tigera.io/learn/guides/kubernetes-security/kubernetes-network-policy/)***

A better option would be to use Kubernetes network policies, which enable you to create rules to allow ingress and egress traffic to a workload based on the labels applied to it. This approach works better than allowing a whole IP address range on the firewall. However, there are cases where the access you need to authorize is not to a fixed IP address. Instead, it is to a service addressed by a domain name, which is very common for API endpoints. In this case, the native Kubernetes network policies cannot help as there is no way to specify an URL address as a parameter in the allow rule; for this, Calico Cloud comes to the rescue. Let’s learn more about creating network security policies to control access based on domain names and all the options to address this issue adequately with Calico Cloud.

***Learn more about Kubernetes networking: [Container Networking: What You Should Know](https://www.tigera.io/learn/guides/kubernetes-networking/container-networking/)***

## Using DNS Policy and Networksets

Calico security policy restricts the utilization of domain names to solely egress’ allow rules. When employing Calico Cloud, connections are permitted to IP addresses obtained from DNS lookups that are exclusively directed to trusted DNS servers. The accepted DNS record types include A, AAAA, and CNAME records. It is essential for the domain name to match exactly—google.com and www.google.com are considered separate entities. Wildcards can be used to match one or more path components at that position. For example,

```
`*.google.com matches www.google.com and www.ipv6.google.com, but not google.com`
```

Let’s see how it works with a real example.

A local application needs to perform API calls to a third-party application that provides information about the weather forecast for a specific location. This third-party application can be accessed by the URL https://api.weather.com/endpoint.

There are multiple ways to indicate permissible domain names within security policies. You have the option to directly specify domain names in either a **GlobalNetworkPolicy** or a namespaced **NetworkPolicy**. Alternatively, you can define domain names within a **GlobalNetworkSet** and refer to this set in a **GlobalNetworkPolicy**, or with a **NetworkSet** and refer to this set in a **NetworkPolicy**.

## Using domain names in GlobalNetworkPolicy and NetworkPolicy

There are two approaches to this method: Using GlobalNetworkPolicy or NetworkPolicy.

The first approach involves creating a **GlobalNetworkPolicy** that includes egress rules with the action set to `Allow`. In the rules, you define the domain names to which outbound traffic is permitted by utilizing the `destination.domains` field.

Here is an example to illustrate this method:

![Screenshot of 'Create Policy' form, showing fields like name, tier, scope, and egress policy rules](https://www.tigera.io/app/uploads/2023/06/Secure-egress-access-with-DNS-Policy-and-NetworkSets2.png)

The YAML would look like this:

```
`apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.allow-egress-to-domain spec: tier: default selector: app == "weather-app" egress: - action: Allow protocol: UDP source: {} destination: ports: - '53' - action: Allow source: {} destination: domains: - api.weather.com types: - Egress`
```

- The first rule authorizes DNS traffic

- The second rule allows connections from any workload in any namespace in the cluster (with labels matching the selector) to the domain api.weather.com.

In the second approach, you would generate a **NetworkPolicy** that incorporates egress rules with the action set to `Allow`. In these rules, you define the domain names to which outbound traffic is permitted by utilizing the `destination.domains` field.

Consider the following example:

![The 'Create Policy' screen, showing an example egress policy allowing UDP traffic to port 53 and traffic to api.weather.com](https://www.tigera.io/app/uploads/2023/06/Secure-egress-access-with-DNS-Policy-and-NetworkSets-3.png)

The YAML would look like this:

```
`apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.allow-egress-to-domain namespace: default spec: tier: default egress: - action: Allow protocol: UDP source: {} destination: ports: - '53' - action: Allow source: {} destination: domains: - api.weather.com types: - Egress`
```

- The initial rule grants permission for DNS traffic

- The following rule permits connections from any workload in a specific namespace in the cluster (matching the selector) to the domain api.weather.com.

In contrast to the **GlobalNetworkPolicy** example, the distinction here lies in the fact that the namespaced **NetworkPolicy** can exclusively provide egress access, limited to the specified domains, for workload endpoints within the **default** namespace.

## Using GlobalNetworkSets and NetworkSets

When you require the same set of domains to be referenced in multiple policies or desire a combination of domains, IPs from workload endpoints and host endpoints as allowed destinations, utilizing a **GlobalNetworkSets** is recommended as best practice. By utilizing a single destination selector within a **GlobalNetworkSets**, you have the potential to match all of these resources efficiently.

To implement this method, follow these steps:

1. Create a **GlobalNetworkSet**: Generate a **GlobalNetworkSet** and specify the allowed destination domain names in the `allowedEgressDomains` field. This set will include the desired domain names.

![Screenshot of creating a GlobalNetworkSet, showing 'api.weather.com' added to the allowedEgressDomains field](https://www.tigera.io/app/uploads/2023/06/image5.png)

2. Create a **GlobalNetworkPolicy**: Create a **GlobalNetworkPolicy** and set the `destination.selector` to match the **GlobalNetworkSet** created in the previous step. This ensures that the policy will be applied to the endpoints that match the domains specified in the **GlobalNetworkSet**.

![GlobalNetworkPolicy creation: Destination selector matches GlobalNetworkSet by applying the label 'app=weather-app'. Scope](https://www.tigera.io/app/uploads/2023/06/Secure-egress-access-with-DNS-Policy-and-NetworkSets-5.png)

The YAML would look like this:

```
`apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.allow-egress-to-domain spec: tier: default selector: app == "weather-app" egress: - action: Allow source: {} destination: selector: color == "red" - Egress`
```

By using this approach, you establish a **GlobalNetworkSet** with the allowed destination domain name and then reference this set by setting the appropriate destination.selector in the **GlobalNetworkPolicy**. This ensures that the policy is applied to the relevant endpoints matching the specified domains in the **GlobalNetworkSet**.

And finally, you can refer to domain names in a **NetworkSet** and use a **NetworkPolicy**.

To utilize domain names in a **NetworkSet**, you can follow this method:

1. Create a **NetworkSet**: Generate a **NetworkSet** and specify the allowed destination domain names in the `allowedEgressDomains` field. This set will include the desired domain names.![The 'Create Network Set' form shows adding 'api.weather.com' to the Domains list](https://www.tigera.io/app/uploads/2023/06/image5-1.png)

2. Create a **NetworkPolicy**: Create a **NetworkPolicy** and set the `destination.selector` to match the **NetworkSet** created in the previous step. This ensures that the policy will be applied to the endpoints that match the domains specified in the **NetworkSet**.

The YAML would look like this:

```
`apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.allow-egress-to-domain namespace: default spec: tier: default selector: app == "weather-app" serviceAccountSelector: '' egress: - action: Allow source: {} destination: selector: color == "yellow" types: - Egress`
```

By following this approach, you can effectively utilize domain names in a **NetworkSet** and create **NetworkPolicies** that align with the specified domains in the set.

## Conclusion

There are several methods for incorporating domain names in network policies when using Calico Cloud. You can restrict domain name usage to egress’ allow rules. You can directly specify allowed domain names within a **GlobalNetworkPolicy** or a namespaced **NetworkPolicy**. Alternatively, you can leverage **GlobalNetworkSets** to define domain names and reference them in **GlobalNetworkPolicies**. Similarly, you can use **NetworkSets** in Calico **NetworkPolicies** to specify allowed destination domain names. By employing these methods, you can effectively control egress traffic and define policies based on domain names, allowing or restricting connections to specific domains or subdomains.

Want to learn more? [Get started with a free Calico Cloud trial](https://link.tigera.io/ueOp4).

[Best Practices](https://www.tigera.io/tags/best-practices/)[How-To](https://www.tigera.io/tags/how-to/)[Products](https://www.tigera.io/tags/products/)

## Related posts

[![Meet Mylo: An AI-native way to work with Calico](https://www.tigera.io/app/uploads/2026/09/Meet-Mylo-An-AI-native-way-to-work-with-Calico.png)](https://www.tigera.io/blog/meet-mylo-an-ai-native-way-to-work-with-calico/)

#### [Meet Mylo: An AI-native way to work with Calico](https://www.tigera.io/blog/meet-mylo-an-ai-native-way-to-work-with-calico/)

By [Phil DiCorpo](https://www.tigera.io/blog/author/phil-dicorpo/)
on Sep 3, 2026

A library of Calico tools and skills — delivered through the Calico MCP Server What if your hardest network question took ten minutes instead of ten days? Anyone who has operated Kubernetes networking at scale...

[Read more](https://www.tigera.io/blog/meet-mylo-an-ai-native-way-to-work-with-calico/)

[![The Safest Place to Run an AI Agent Is On a Cluster That Doesn’t Trust It](https://www.tigera.io/app/uploads/2026/08/The-Safest-Place-to-Run-an-AI-Agent-Is-On-a-Cluster-That-Doesnt-Trust-It.png)](https://www.tigera.io/blog/the-safest-place-to-run-an-ai-agent-is-on-a-cluster-that-doesnt-trust-it/)

#### [The Safest Place to Run an AI Agent Is On a Cluster That Doesn’t Trust It](https://www.tigera.io/blog/the-safest-place-to-run-an-ai-agent-is-on-a-cluster-that-doesnt-trust-it/)

By [Alister Baroi](https://www.tigera.io/blog/author/alister-baroi/)
on Aug 27, 2026

Every organization running AI agents has already made a hosting decision. Most made it by accident. The sales team switched on the agent built into their CRM. Engineering is piloting a coding agent in a...

[Read more](https://www.tigera.io/blog/the-safest-place-to-run-an-ai-agent-is-on-a-cluster-that-doesnt-trust-it/)

[![AI Red Team Agents Automate Attacks on your AI Agents. Runtime Policies Automate their Defense.](https://www.tigera.io/app/uploads/2026/08/AI-Red-Team-Agents-Automate-Attacks-on-your-AI-Agents.-Runtime-Policies-Automate-their-Defense.png)](https://www.tigera.io/blog/ai-red-team-agents-automate-attacks-on-your-ai-agents-runtime-policies-automate-their-defense/)

#### [AI Red Team Agents Automate Attacks on your AI Agents. Runtime Policies Automate their Defense.](https://www.tigera.io/blog/ai-red-team-agents-automate-attacks-on-your-ai-agents-runtime-policies-automate-their-defense/)

By [Alister Baroi](https://www.tigera.io/blog/author/alister-baroi/)
on Aug 24, 2026

The AI red teaming market grew up fast this year. OpenAI bought Promptfoo, Cisco and Microsoft shipped automated attack suites, and a seed-stage startup publicly compromised 50 of 55 live customer service bots. These platforms...

[Read more](https://www.tigera.io/blog/ai-red-team-agents-automate-attacks-on-your-ai-agents-runtime-policies-automate-their-defense/)

<!-- plugin=object-cache-pro client=phpredis metric#hits=3250 metric#misses=34 metric#hit-ratio=99.0 metric#bytes=1568419 metric#prefetches=0 metric#store-reads=176 metric#store-writes=20 metric#store-hits=179 metric#store-misses=23 metric#sql-queries=36 metric#ms-total=577.80 metric#ms-cache=32.52 metric#ms-cache-avg=0.1668 metric#ms-cache-ratio=5.6 sample#redis-hits=2593123 sample#redis-misses=903193 sample#redis-hit-ratio=74.2 sample#redis-ops-per-sec=65 sample#redis-evicted-keys=0 sample#redis-used-memory=150282200 sample#redis-used-memory-rss=116187136 sample#redis-memory-fragmentation-ratio=0.8 sample#redis-connected-clients=1 sample#redis-tracking-clients=0 sample#redis-rejected-connections=0 sample#redis-keys=126957 -->
