{"id":20410282,"url":"https://github.com/kuadrant/dns-operator","last_synced_at":"2026-02-05T15:01:49.776Z","repository":{"id":220502222,"uuid":"751798519","full_name":"Kuadrant/dns-operator","owner":"Kuadrant","description":"Kuadrant DNS Operator","archived":false,"fork":false,"pushed_at":"2025-06-19T16:36:13.000Z","size":1214,"stargazers_count":4,"open_issues_count":75,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-19T16:36:32.129Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kuadrant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-02T10:57:08.000Z","updated_at":"2025-06-15T11:26:50.000Z","dependencies_parsed_at":"2024-11-07T12:31:03.543Z","dependency_job_id":"1b3ac49f-b2c5-43cc-be0a-f014e494e669","html_url":"https://github.com/Kuadrant/dns-operator","commit_stats":null,"previous_names":["kuadrant/kuadrant-dns-operator","kuadrant/dns-operator"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/Kuadrant/dns-operator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fdns-operator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fdns-operator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fdns-operator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fdns-operator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kuadrant","download_url":"https://codeload.github.com/Kuadrant/dns-operator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fdns-operator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261584215,"owners_count":23180673,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T05:45:18.870Z","updated_at":"2026-02-05T15:01:49.767Z","avatar_url":"https://github.com/Kuadrant.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DNS Operator\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FKuadrant%2Fdns-operator.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FKuadrant%2Fdns-operator?ref=badge_shield)\n\n\nThe DNS Operator is a kubernetes based controller responsible for reconciling DNS Record custom resources. It interfaces with cloud DNS providers such as AWS, Google and Azure to bring the DNS zone into the state declared in these CRDs.\nOne of the key use cases the DNS operator solves, is allowing complex DNS routing strategies such as Geo and Weighted to be expressed. This allows you to leverage DNS as the first layer of traffic management. These strategies increase in value as you works across multiple clusters. DNS operator can be deployed to multiple cluster and coordinate on a given zone allowing you to use a shared domain name to balance traffic based on your requirements.\n\n## Getting Started\n\n### Pre Setup\n\n#### Add DNS provider configuration\n\n**NOTE:** You can optionally skip this step but at least one DNS Provider Secret will need to be configured with valid credentials to use the DNS Operator.\n\n##### AWS Provider (Route53)\n```bash\nmake local-setup-aws-clean local-setup-aws-generate AWS_ACCESS_KEY_ID=\u003cMy AWS ACCESS KEY\u003e AWS_SECRET_ACCESS_KEY=\u003cMy AWS Secret Access Key\u003e\n```\nMore details about the AWS provider can be found [here](./docs/provider.md#aws-route-53-provider)\n\n##### GCP Provider\n\n```bash\nmake local-setup-gcp-clean local-setup-gcp-generate GCP_GOOGLE_CREDENTIALS='\u003cMy GCP Credentials.json\u003e' GCP_PROJECT_ID=\u003cMy GCP PROJECT ID\u003e\n```\nMore details about the GCP provider can be found [here](./docs/provider.md#google-cloud-dns-provider)\n\n##### AZURE Provider\n\n```bash\nmake local-setup-azure-clean local-setup-azure-generate KUADRANT_AZURE_CREDENTIALS='\u003cMy Azure Credentials.json\u003e'\n```\n\nInfo on generating service principal credentials [here](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/azure.md)\n\nGet your resource group ID like so:\n```\naz group show --resource-group \u003cresource group name\u003e | jq \".id\" -r\n```\n\nAlso give traffic manager contributor role:\n```\naz role assignment create --role \"Traffic Manager Contributor\" --assignee $EXTERNALDNS_SP_APP_ID --scope \u003cRESOURCE_GROUP_ID\u003e\n```\n\nGetting the zone ID can be achieved using the below command:\n```bash\naz network dns zone show --name \u003cmy domain name\u003e --resource-group \u003cmy resource group\u003e --query \"{id:id,domain:name}\"\n```\n\n### Running controller locally (default)\n\n1. Create local environment(creates kind cluster)\n```sh\nmake local-setup\n```\n\n**Note:** When using DNS Groups functionality with CoreDNS, you can customize where the active groups TXT record is resolved from:\n```sh\nmake local-setup EXTERNAL_GROUPS_HOST=\u003cyour-external-host\u003e\n```\nDefault: `kuadrant-active-groups.hcpapps.net`\n\n1. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):\n\n```sh\nmake run\n```\n\nTo run with a specific group identifier:\n```sh\nmake run GROUP=\u003cyour-group-name\u003e\n```\n\n### Running controller on the cluster\n\n1. Create local environment(creates kind cluster)\n```sh\nmake local-setup DEPLOY=true\n```\n\n#### CoreDNS with DNS Groups\n\nAfter running the local-setup, some configuration is required to enable the active-groups TXT record\nwhen running locally with CoreDNS and using DNS Groups.\n\n1. Modify the corefile\nYou will need to modify the Corefile in coredns, this is located in a configmap `kuadrant-coredns`\n by default in the `kuadrant-coredns` namespace. \n \n Add the following config (change the parts in caps to your usecase):\n```sh\n    rewrite name regex kuadrant-active-groups\\.(.*)ZONE_DOMAIN_NAME EXTERNAL_TXT_RECORD_HOST\n    forward EXTERNAL_TXT_RECORD_HOST /etc/resolv.conf\n```\n\nBefore the `ready` statement.\n\nThen restart the coredns operator.\n```sh\nkubectl -n kuadrant-coredns rollout restart deployment kuadrant-coredns \n```\n\n1. Verify controller deployment\n```sh\nkubectl logs -f deployments/dns-operator-controller-manager -n dns-operator-system\n```\n\n1. Create External record:\nIn your dns provider, create the record referred to earlier in your corefile. This record requires the \nfollowing format (changing the values in caps as required):\n```sh\nversion=1;groups=GROUP1\u0026\u0026GROUP2\n```\n\n1. Verify coredns groups resolution\n\nOnce the external record has been created, find the local IP of the coredns instance:\n```sh\nNS=$(kubectl get secrets -n dnstest dns-provider-credentials-coredns -o yaml | yq \".data.NAMESERVERS\" | base64 -d | cut -f1 -d\":\")\n```\nThen dig the local TXT record from that IP:\n```sh\ndig @$NS kuadrant-active-groups.k.example.com TXT +short\n\"version=1;groups=GROUP1\u0026\u0026GROUP2\"\n```\n\n### Running controller on existing cluster\n\nYou'll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.\n**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).\n\n1. Apply Operator manifests\n```sh\nkustomize build config/default | kubectl apply -f -\n```\n\n1. Verify controller deployment\n```sh\nkubectl logs -f deployments/dns-operator-controller-manager -n dns-operator-system\n```\n\n## CoreDNS Provider\n\nThe DNS Operator includes a CoreDNS plugin that enables serving DNS zone data from Kubernetes DNSRecord resources. This is particularly useful for local development and testing.\n\n### CoreDNS Plugin Configuration\n\nFor detailed CoreDNS configuration and integration documentation, see:\n- [CoreDNS Integration Guide](./docs/coredns/README.md)\n\n## Development\n\n### E2E Test Suite\n\nThe e2e test suite can be executed against any cluster running the DNS Operator with configuration added for any supported provider.\n\n```\nmake test-e2e TEST_DNS_ZONE_DOMAIN_NAME=\u003cMy domain name\u003e TEST_DNS_PROVIDER_SECRET_NAME=\u003cMy provider secret name\u003e TEST_DNS_NAMESPACES=\u003cMy test namespace(s)\u003e\n```\n\n| Environment Variable          | Description                                                                                                                                                                         |\n|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| TEST_DNS_PROVIDER_SECRET_NAME | Name of the provider secret to use. If using local-setup provider secrets zones, one of [dns-provider-credentials-aws; dns-provider-credentials-gcp;dns-provider-credentials-azure] | \n| TEST_DNS_ZONE_DOMAIN_NAME     | The Domain name to use in the test. Must be a zone accessible with the (TEST_DNS_PROVIDER_SECRET_NAME) credentials with the same domain name                                        | \n| TEST_DNS_NAMESPACES           | The namespace(s) where the provider secret(s) can be found                                                                                                                          | \n\n### Modifying the API definitions\nIf you are editing the API definitions, generate the manifests such as CRs or CRDs using:\n\n```sh\nmake manifests\n```\n\n**NOTE:** Run `make --help` for more information on all potential `make` targets\n\nMore information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)\n\n## Controller flags and environmental variables\nThe controller can be started with any of the following flags or environmental variables. Upon the start of the controller operator give precedence to envars (i.e. `PROVIDER` envar will override `--provider` flag). If neither is set the default value will be used. Envars are parsed into their types from a string where applicable. If parsing fails - envar is ignored\n\n| Flag Name                 | Flag Type     | Envar Name                | Description                                                                                                           | Default                               |\n|---------------------------|---------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------|\n| metrics-bind-address      | string        | METRICS_BIND_ADDRESS      | The address the metric endpoint binds to.                                                                             | \":8080\"                               |\n| health-probe-bind-address | string        | HEALTH_PROBE_BIND_ADDRESS | The address the probe endpoint binds to.                                                                              | \":8081\"                               |\n| pprof-bind-address        | string        | PPROF_BIND_ADDRESS        | The address the pprof endpoint binds to.                                                                              | \":8082\"                               |\n| leader-elect              | bool          | LEADER_ELECT              | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. | \"false\"                               |\n| min-requeue-time          | time.Duration | MIN_REQUEUE_TIME          | The minimal timeout between calls to the DNS Provider. Controls if we commit to the full reconcile loop               | \"5s\"                                  |\n| max-requeue-time          | time.Duration | MAX_REQUEUE_TIME          | The maximum times it takes between reconciliations of DNS Record. Controls how ofter record is reconciled.            | \"15m\"                                 |\n| valid-for                 | time.Duration | VALID_FOR                 | Duration when the record is considered to hold valid information. Controls if we commit to the full reconcile loop    | \"14m\"                                 |\n| provider                  | string        | PROVIDER                  | DNS Provider(s) to enable.                                                                                            | \"aws,google,azure,coredns,endpoints\"  |\n| enable-probes             | bool          | ENABLE_PROBES             | Enable DNSHealthProbes controller.                                                                                    | \"true\"                                |\n| insecure-health-checks    | bool          | INSECURE_HEALTH_CHECKS    | DNS Health Probes will ignore insecure certificates when true                                                         | \"true\"                                |\n| cluster-secret-namespace  | string        | CLUSTER_SECRET_NAMESPACE  | The namespace in which cluster secrets are located                                                                    | \"dns-operator-system\"                 |\n| cluster-secret-label      | string        | CLUSTER_SECRET_LABEL      | The label that identifies a Secret resource as a cluster secret.                                                      | \"kuadrant.io/multicluster-kubeconfig\" |\n| watch-namespaces          | string        | WATCH_NAMESPACES          | Comma separated list of default namespaces.                                                                           | \\\u003cempty string\\\u003e                      |\n| delegation-role           | string        | DELEGATION_ROLE           | The delegation role for this controller. Must be one of 'primary'(default), or 'secondary'                            | \"primary\"                             |\n| group                     | string        | GROUP                     | The DNS failover group identifier for this controller instance. Used for active-passive failover across clusters.     | \\\u003cempty string\\\u003e                      |\n\n## Logging\nLogs are following the general guidelines: \n- `logger.Info()` describe a high-level state of the resource such as creation, deletion and which reconciliation path was taken.  \n- `logger.Error()` describe only those errors that are not returned in the result of the reconciliation. If error is occurred there should be only one error message. \n- `logger.V(1).Info()` debug level logs to give information about every change or event caused by the resource as well as every update of the resource.\n\nThere are two flags to control logging output \n- `--log-mode=[development|\u003cany-other-value\u003e]` will enable debug level logs for the output. \n    The debug mode is the most verbose.\n- `--log-level` controls the level of displayed logs. Defaults to the most verbose in the `development` mode. \n    In any other modes it can take numerical values form `-1` (Debug level) to `4` (Nothing). \n    It is possible to specify other values, but hey will have no effect (e.g. `4` will do the same as `128`) \n\nYou can find more [here](https://pkg.go.dev/github.com/go-logr/zapr#section-readme).\n\n\n### Common metadata\nNot exhaustive list of metadata for DNSRecord controller:\n- `level` - logging level. Values are: `info`,`debug` or `error`\n- `ts` - timestamp\n- `logger` - logger name\n- `msg`\n- `controller` and `controllerKind` - controller name, and it's kind respectively to output the log\n- `DNSRecord` - name and namespace of the DNS Record CR that is being reconciled\n- `reconcileID`\n- `ownerID` - ID the of owner of the DNS Record\n- `txtPrefix`/`txtSuffix` - prefix and suffix of the TXT record in provider.\n- `zoneEndpoints` - endpoints that exist in the provider\n- `specEndpoints` - endpoints defined in the spec\n- `statusEndpoints` - endpoints that were processed previously\n- `currentGroup` - the group this DNS operator instance belongs to\n- `activeGroups` - the currently active groups read from DNS\n\n\u003e Note that not all the metadata values are present at each of the logs statements. \n\n### Examples\nTo query logs locally you can use `jq`. For example:\nRetrieve logs by \n```shell\nkubectl get deployments -l app.kubernetes.io/part-of=dns-operator -A\n\nNAMESPACE             NAME                              READY \ndns-operator-system   dns-operator-controller-manager   1/1   \n```\nAnd query them. For example:\n```shell\nkubectl logs -l control-plane=dns-operator-controller-manager -n dns-operator-system --tail -1 | sed '/^{/!d' | jq 'select(.controller==\"dnsrecord\" and .level==\"info\")'\n```\nor \n```shell\nkubectl logs -l control-plane=dns-operator-controller-manager -n dns-operator-system --tail -1 | sed '/^{/!d' | jq 'select(.controller==\"dnsrecord\" and .DNSRecord.name==\"test\" and .reconcileID==\"2be16b6d-b90f-430e-9996-8b5ec4855d53\")' | jq '.level, .msg, .zoneEndpoints, .specEndpoints, .statusEndpoints '\n\n```\nYou could use selector in the `jq` with `and`/`not`/`or` to restrict.\n\n\n## License\n\nCopyright 2024.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FKuadrant%2Fdns-operator.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FKuadrant%2Fdns-operator?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuadrant%2Fdns-operator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkuadrant%2Fdns-operator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuadrant%2Fdns-operator/lists"}