Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into bugfix/group-endpoi…
Browse files Browse the repository at this point in the history
…nts-per-hosted-zone-for-aws
  • Loading branch information
leonardocaylent committed Apr 22, 2024
2 parents d9b7439 + de08c4e commit 05ca35e
Show file tree
Hide file tree
Showing 13 changed files with 293 additions and 137 deletions.
26 changes: 11 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,20 @@ Known providers using webhooks:

| Provider | Repo |
| -------- | ----------- |
| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
| GleSYS | https://github.com/glesys/external-dns-glesys |
| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
| Gcore | https://github.com/G-Core/external-dns-gcore-webhook |
| GleSYS | https://github.com/glesys/external-dns-glesys |
| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |

## Status of providers
## Status of in-tree providers

ExternalDNS supports multiple DNS providers which have been implemented by the [ExternalDNS contributors](https://github.com/kubernetes-sigs/external-dns/graphs/contributors). Maintaining all of those in a central repository is a challenge and we have limited resources to test changes. This means that it is very hard to test all providers for possible regressions and, as written in the [Contributing](#Contributing) section, we encourage contributors to step in as maintainers for the individual providers and help by testing the integrations.
ExternalDNS supports multiple DNS providers which have been implemented by the [ExternalDNS contributors](https://github.com/kubernetes-sigs/external-dns/graphs/contributors). Maintaining all of those in a central repository is a challenge, which introduces lots of toil and potential risks.

End-to-end testing of ExternalDNS is currently
[performed](https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/test/e2e/external_dns.go)
in the separate
[kubernetes-on-aws](https://github.com/zalando-incubator/kubernetes-on-aws)
repository.
This mean that `external-dns` has begun the process to move providers out of tree. See #4347 for more details. Those who are interested can create a webhook provider based on an _in-tree_ provider and after submit a PR to reference it here.

We define the following stability levels for providers:

Expand All @@ -112,7 +108,7 @@ The following table clarifies the current status of the providers according to t
| AWS Route 53 | Stable | |
| AWS Cloud Map | Beta | |
| Akamai Edge DNS | Beta | |
| AzureDNS | Beta | |
| AzureDNS | Stable | |
| BlueCat | Alpha | @seanmalloy @vinny-sabatini |
| Civo | Alpha | @alejandrojnm |
| CloudFlare | Beta | |
Expand Down
4 changes: 4 additions & 0 deletions charts/external-dns/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [UNRELEASED]

## Added

- Added support for setting `excludeDomains` argument. ([#4380](https://github.com/kubernetes-sigs/external-dns/pull/4380))[@bford-evs](https://github.com/bford-evs)

## [v1.14.4] - 2023-04-03

### Added
Expand Down
1 change: 1 addition & 0 deletions charts/external-dns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| dnsPolicy | string | `nil` | [DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod, if not set the default will be used. |
| domainFilters | list | `[]` | |
| env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container. |
| excludeDomains | list | `[]` | |
| extraArgs | list | `[]` | Extra arguments to provide to _ExternalDNS_. |
| extraVolumeMounts | list | `[]` | Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `external-dns` container. |
| extraVolumes | list | `[]` | Extra [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) for the `Pod`. |
Expand Down
3 changes: 3 additions & 0 deletions charts/external-dns/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ spec:
{{- range .Values.domainFilters }}
- --domain-filter={{ . }}
{{- end }}
{{- range .Values.excludeDomains }}
- --exclude-domains={{ . }}
{{- end }}
- --provider={{ $providerName }}
{{- range .Values.extraArgs }}
- {{ tpl . $ }}
Expand Down
3 changes: 3 additions & 0 deletions charts/external-dns/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ txtSuffix:
## - Limit possible target zones by domain suffixes.
domainFilters: []

## -- Intentionally exclude domains from being managed.
excludeDomains: []

provider:
# -- _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).
name: aws
Expand Down
12 changes: 9 additions & 3 deletions docs/tutorials/aws.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,12 @@ aws iam attach-user-policy --user-name "externaldns" --policy-arn $POLICY_ARN

```bash
SECRET_ACCESS_KEY=$(aws iam create-access-key --user-name "externaldns")
cat <<-EOF > /local/path/to/credentials
ACCESS_KEY_ID=$(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.AccessKeyId')

cat <<-EOF > credentials
[default]
aws_access_key_id = $(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.AccessKeyId')
aws_access_key_id = $(echo $ACCESS_KEY_ID)
aws_secret_access_key = $(echo $SECRET_ACCESS_KEY | jq -r '.AccessKey.SecretAccessKey')
EOF
```
Expand Down Expand Up @@ -910,13 +912,17 @@ eksctl delete cluster --name $EKS_CLUSTER_NAME --region $EKS_CLUSTER_REGION
Give ExternalDNS some time to clean up the DNS records for you. Then delete the hosted zone if you created one for the testing purpose.

```bash
aws route53 delete-hosted-zone --id $NODE_ID # e.g /hostedzone/ZEWFWZ4R16P7IB
aws route53 delete-hosted-zone --id $ZONE_ID # e.g /hostedzone/ZEWFWZ4R16P7IB
```

If IAM user credentials were used, you can remove the user with:

```bash
aws iam detach-user-policy --user-name "externaldns" --policy-arn $POLICY_ARN
# If static credentials were used
aws iam delete-access-key --user-name "externaldns" --access-key-id $ACCESS_KEY_ID
aws iam delete-user --user-name "externaldns"
```

Expand Down
123 changes: 89 additions & 34 deletions docs/tutorials/gke.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This solution will only work when both CloudDNS and GKE are provisioned in the s

### Configure Project Environment

Setup your environment to work with Google Cloud Platform. Fill in your variables as needed, e.g. target project.
Set up your environment to work with Google Cloud Platform. Fill in your variables as needed, e.g. target project.

```bash
# set variables to the appropriate desired values
Expand Down Expand Up @@ -151,6 +151,91 @@ gcloud container clusters create $GKE_CLUSTER_NAME \
--workload-pool "$GKE_PROJECT_ID.svc.id.goog"
```

### Workload Identity

[Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) allows workloads in your GKE cluster to [authenticate directly to GCP](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity#credential-flow) using Kubernetes Service Accounts

You have an option to chose from using the gcloud CLI or using Terraform.

=== "gcloud CLI"

The below instructions assume you are using the default Kubernetes Service account name of `external-dns` in the namespace `external-dns`

Grant the Kubernetes service account DNS `roles/dns.admin` at project level

```shell
gcloud projects add-iam-policy-binding projects/DNS_PROJECT_ID \
--role=roles/dns.admin \
--member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/external-dns/sa/external-dns \
--condition=None
```

Replace the following:

* `DNS_PROJECT_ID` : Project ID of your DNS project. If DNS is in the same project as your GKE cluster, use your GKE project.
* `PROJECT_ID`: your Google Cloud project ID of your GKE Cluster
* `PROJECT_NUMBER`: your numerical Google Cloud project number of your GKE cluster

If you wish to change the namespace, replace

* `ns/external-dns` with `ns/<your namespace`
* `sa/external-dns` with `sa/<your ksa>`



=== "Terraform"

The below instructions assume you are using the default Kubernetes Service account name of `external-dns` in the namespace `external-dns`

Create a file called `main.tf` and place in it the below. _Note: If you're an experienced terraform user feel free to split these out in to different files_

```hcl
variable "gke-project" {
type = string
description = "Name of the project that the GKE cluster exists in"
default = "GKE-PROJECT"
}

variable "ksa_name" {
type = string
description = "Name of the Kubernetes service account that will be accessing the DNS Zones"
default = "external-dns"
}

variable "kns_name" {
type = string
description = "Name of the Kubernetes Namespace"
default = "external-dns"
}

data "google_project" "project" {
project_id = var.gke-project
}

locals {
member = "principal://iam.googleapis.com/projects/${data.google_project.project.number}/locations/global/workloadIdentityPools/${var.gke-project}.svc.id.goog/subject/ns/${var.kns_name}/sa/${var.ksa_name}"
}

resource "google_project_iam_member" "external_dns" {
member = local.member
project = "DNS-PROJECT"
role = "roles/dns.admin"
}
```

Replace the following

* `GKE-PROJECT` : Project that contains your GKE cluster
* `DNS-PROJECT` : Project that holds your DNS zones

You can also change the below if you plan to use a different service account name and namespace

* `variable "ksa_name"` : Name of the Kubernetes service account external-dns will use
* `variable "kns_name"` : Name of the Kubernetes Name Space that will have external-dns installed to




### Worker Node Service Account method

In this method, the GSA (Google Service Account) that is associated with GKE worker nodes will be configured to have access to Cloud DNS.
Expand Down Expand Up @@ -206,47 +291,17 @@ kubectl create secret generic "external-dns" --namespace ${EXTERNALDNS_NS:-"defa
```

After this, follow the steps in [Deploy ExternalDNS](#deploy-externaldns). Make sure to set the `--google-project` flag to match Cloud DNS project name. Make sure to uncomment out the section that mounts the secret to the ExternalDNS pods.
### Workload Identity

[Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) allows workloads in your GKE cluster to impersonate GSA (Google Service Accounts) using KSA (Kubernetes Service Accounts) configured during deployemnt. These are the steps to use this feature with ExternalDNS.

#### Create GSA for use with Workload Identity

```bash
DNS_SA_NAME="external-dns-sa"
DNS_SA_EMAIL="$DNS_SA_NAME@${GKE_PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts create $DNS_SA_NAME --display-name $DNS_SA_NAME
gcloud projects add-iam-policy-binding $DNS_PROJECT_ID \
--member serviceAccount:$DNS_SA_EMAIL --role "roles/dns.admin"
```

#### Link KSA to GSA

Add an IAM policy binding bewtween the workload identity GSA and ExternalDNS GSA. This will link the ExternalDNS KSA to ExternalDNS GSA.

```bash
gcloud iam service-accounts add-iam-policy-binding $DNS_SA_EMAIL \
--role "roles/iam.workloadIdentityUser" \
--member "serviceAccount:$GKE_PROJECT_ID.svc.id.goog[${EXTERNALDNS_NS:-"default"}/external-dns]"
```

#### Deploy External DNS

Deploy ExternalDNS with the following steps below, documented under [Deploy ExternalDNS](#deploy-externaldns). Set the `--google-project` flag to the Cloud DNS project name.

#### Link KSA to GSA in Kubernetes

Add the proper workload identity annotation to the ExternalDNS KSA.

```bash
kubectl annotate serviceaccount "external-dns" \
--namespace ${EXTERNALDNS_NS:-"default"} \
"iam.gke.io/gcp-service-account=$DNS_SA_EMAIL"
```

#### Update ExternalDNS pods

!!! note "Only required if not enabled on all nodes"
If you have GKE Workload Identity enabled on all nodes in your cluster, the below step is not necessary

Update the Pod spec to schedule the workloads on nodes that use Workload Identity and to use the annotated Kubernetes service account.

```bash
Expand Down
33 changes: 32 additions & 1 deletion endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,17 @@ func (t Targets) Len() int {
}

func (t Targets) Less(i, j int) bool {
return t[i] < t[j]
ipi, err := netip.ParseAddr(t[i])
if err != nil {
return t[i] < t[j]
}

ipj, err := netip.ParseAddr(t[j])
if err != nil {
return t[i] < t[j]
}

return ipi.String() < ipj.String()
}

func (t Targets) Swap(i, j int) {
Expand All @@ -92,6 +102,27 @@ func (t Targets) Same(o Targets) bool {

for i, e := range t {
if !strings.EqualFold(e, o[i]) {
// IPv6 can be shortened, so it should be parsed for equality checking
ipA, err := netip.ParseAddr(e)
if err != nil {
log.WithFields(log.Fields{
"targets": t,
"comparisonTargets": o,
}).Debugf("Couldn't parse %s as an IP address: %v", e, err)
}

ipB, err := netip.ParseAddr(o[i])
if err != nil {
log.WithFields(log.Fields{
"targets": t,
"comparisonTargets": o,
}).Debugf("Couldn't parse %s as an IP address: %v", e, err)
}

// IPv6 Address Shortener == IPv6 Address Expander
if ipA.IsValid() && ipB.IsValid() {
return ipA.String() == ipB.String()
}
return false
}
}
Expand Down
36 changes: 36 additions & 0 deletions endpoint/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func TestTargetsSame(t *testing.T) {
{""},
{"1.2.3.4"},
{"8.8.8.8", "8.8.4.4"},
{"dd:dd::01", "::1", "::0001"},
{"example.org", "EXAMPLE.ORG"},
}

Expand All @@ -51,6 +52,37 @@ func TestTargetsSame(t *testing.T) {
}
}

func TestSameSuccess(t *testing.T) {
tests := []struct {
a Targets
b Targets
}{
{
[]string{"::1"},
[]string{"::0001"},
},
{
[]string{"::1", "dd:dd::01"},
[]string{"dd:00dd::0001", "::0001"},
},

{
[]string{"::1", "dd:dd::01"},
[]string{"00dd:dd::0001", "::0001"},
},
{
[]string{"::1", "1.1.1.1", "2600.com", "3.3.3.3"},
[]string{"2600.com", "::0001", "3.3.3.3", "1.1.1.1"},
},
}

for _, d := range tests {
if d.a.Same(d.b) == false {
t.Errorf("%#v should equal %#v", d.a, d.b)
}
}
}

func TestSameFailures(t *testing.T) {
tests := []struct {
a Targets
Expand All @@ -69,6 +101,10 @@ func TestSameFailures(t *testing.T) {
[]string{"1.2.3.4", "4.3.2.1"},
[]string{"8.8.8.8", "8.8.4.4"},
},
{
[]string{"::1", "2600.com", "3.3.3.3"},
[]string{"2600.com", "3.3.3.3", "1.1.1.1"},
},
}

for _, d := range tests {
Expand Down
Loading

0 comments on commit 05ca35e

Please sign in to comment.