mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Merge pull request #3652 from johngmyers/registry-doc
Improve registry documentation
This commit is contained in:
commit
3dfec6b30d
11
README.md
11
README.md
@ -63,11 +63,10 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz
|
||||
* [Plural](https://www.plural.sh/)
|
||||
* [Pi-hole](https://pi-hole.net/)
|
||||
|
||||
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||
ExternalDNS is, by default, aware of the records it is managing, therefore it can safely manage non-empty hosted zones. We strongly encourage you to set `--txt-owner-id` to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||
|
||||
Note that all flags can be replaced with environment variables; for instance,
|
||||
`--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`, or
|
||||
`--registry txt` could be replaced with `EXTERNAL_DNS_REGISTRY=txt`.
|
||||
`--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`.
|
||||
|
||||
## Status of providers
|
||||
|
||||
@ -237,17 +236,17 @@ If the service is not of type Loadbalancer you need the --publish-internal-servi
|
||||
Locally run a single sync loop of ExternalDNS.
|
||||
|
||||
```console
|
||||
external-dns --registry txt --txt-owner-id my-cluster-id --provider google --google-project example-project --source service --once --dry-run
|
||||
external-dns --txt-owner-id my-cluster-id --provider google --google-project example-project --source service --once --dry-run
|
||||
```
|
||||
|
||||
This should output the DNS records it will modify to match the managed zone with the DNS records you desire. It also assumes you are running in the `default` namespace. See the [FAQ](docs/faq.md) for more information regarding namespaces.
|
||||
|
||||
Note: TXT records will have `my-cluster-id` value embedded. Those are used to ensure that ExternalDNS is aware of the records it manages.
|
||||
Note: TXT records will have the `my-cluster-id` value embedded. Those are used to ensure that ExternalDNS is aware of the records it manages.
|
||||
|
||||
Once you're satisfied with the result, you can run ExternalDNS like you would run it in your cluster: as a control loop, and **not in dry-run** mode:
|
||||
|
||||
```console
|
||||
external-dns --registry txt --txt-owner-id my-cluster-id --provider google --google-project example-project --source service
|
||||
external-dns --txt-owner-id my-cluster-id --provider google --google-project example-project --source service
|
||||
```
|
||||
|
||||
Check that ExternalDNS has created the desired DNS record for your Service and that it points to its load balancer's IP. Then try to resolve it:
|
||||
|
@ -1,124 +0,0 @@
|
||||
# Registry
|
||||
#### [Old name: storage]
|
||||
|
||||
Initial discussion - https://github.com/kubernetes-sigs/external-dns/issues/44
|
||||
|
||||
## Purpose
|
||||
|
||||
One should not be afraid to use external-dns, because it can delete or overwrite the records preexisting in the DNS provider.
|
||||
|
||||
**Why we need it?**
|
||||
|
||||
DNS provider (AWS Route53, Google DNS, etc.) stores dns records which are created via various means. Integration of External-DNS should be safe and should not delete or overwrite the records which it is not responsible for. Moreover, it should certainly be possible for multiple kubernetes clusters to share the same hosted zone within the dns provider, additionally multiple external-dns instances inside the same cluster should be able to co-exist without messing with the same set of records.
|
||||
|
||||
Registry provides a mechanism to ensure the safe management of DNS records
|
||||
|
||||
This proposal introduces multiple possible implementation with the details depending on the setup.
|
||||
|
||||
## Requirements and assumptions
|
||||
|
||||
1. Pre-existing records should not be modified by external-dns
|
||||
2. External-dns instance only creates/modifies/deletes records which are created by this instance
|
||||
3. It should be possible to transfer the ownership to another external-dns instance
|
||||
4. ~~Any integrated DNS provider should provide at least a single way to implement the registry~~ Noop registry can be used to disable ownership
|
||||
5. Lifetime of the records should not be limited to lifetime of external-dns
|
||||
6. External-dns should have its identifier for marking the managed records - **`owner-id`**
|
||||
|
||||
## Types of registry
|
||||
|
||||
The following presents two ways to implement the registry, and we are planning to implement both for compatibility purposes.
|
||||
|
||||
### TXT records
|
||||
|
||||
This implementation idea is borrowed from [Mate](https://github.com/linki/mate/)
|
||||
|
||||
Each record created by external-dns is accompanied by the TXT record, which internally stores the external-dns identifier. For example, if external dns with `owner-id="external-dns-1"` record to be created with dns name `foo.zone.org`, external-dns will create a TXT record with the same dns name `<record_type>-foo.zone.org` and injected value of `"external-dns-1"`. The transfer of ownership can be done by modifying the value of the TXT record. If no TXT record exists for the record or the value does not match its own `owner-id`, then external-dns will simply ignore it.
|
||||
|
||||
#### Goods
|
||||
1. Easy to guarantee cross-cluster ownership safety
|
||||
2. Data lifetime is not limited to cluster or external-dns lifetime
|
||||
3. Supported by major DNS providers
|
||||
4. TXT record are created alongside other records in a batch request. Hence **eliminating possibility of having inconsistent ownership information and dns provider state**
|
||||
|
||||
#### Bads
|
||||
1. TXT record cannot co-exist with CNAME records (this can be solved by creating a TXT record with another domain name, e.g. `foo.org->foo.txt.org`)
|
||||
2. Introduces complexity to the logic
|
||||
3. Difficult to do the transfer of ownership
|
||||
4. Too easy to mess up with manual modifications
|
||||
|
||||
### ConfigMap
|
||||
|
||||
**This implementation cannot be considered 100% error free**, hence use with caution [see **Possible failure scenario** below]
|
||||
|
||||
Store the state in the configmap. ConfigMap is created and managed by each external-dns individually, i.e. external-dns with **`owner-id=external-dns-1`** will create and operate on `extern-dns-1-registry` ConfigMap. ConfigMap will store **all** the records present in the DNS provider as serialized JSON. For example:
|
||||
|
||||
```
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
creationTimestamp: 2016-03-09T19:14:38Z
|
||||
name: external-dns-1-storage
|
||||
namespace: same-as-external-dns-1
|
||||
data:
|
||||
records: "[{
|
||||
\"dnsname\": \"foo.org\",
|
||||
\"owner\": \"external-dns-1\",
|
||||
\"target\": \"loadbalancer1.com\",
|
||||
\"type\": \"A\"
|
||||
}, {
|
||||
\"dnsname\": \"bar.org\",
|
||||
\"owner\": \"external-dns-2\",
|
||||
\"target\": \"loadbalancer2.com\",
|
||||
\"type\": \"A\"
|
||||
}, {
|
||||
\"dnsname\": \"unmanaged.org\",
|
||||
\"owner\": \"\",
|
||||
\"target\": \"loadbalancer2.com\",
|
||||
\"type\": \"CNAME\"
|
||||
}]"
|
||||
|
||||
```
|
||||
|
||||
ConfigMap will be periodically resynced with the dns provider by fetching the dns records and comparing it with the data currently stored and hence rebuilding the ownership information.
|
||||
|
||||
#### Goods
|
||||
1. Not difficult to implement and easy to do the ownership transfer
|
||||
2. ConfigMap is a first class citizen in kubernetes world
|
||||
3. Does not create dependency/restriction on DNS provider
|
||||
4. Cannot be easily messed with by other parties
|
||||
|
||||
#### Bads
|
||||
1. ConfigMap might be out of sync with dns provider state
|
||||
2. LifeTime is obviously limited to the cluster lifetime
|
||||
3. Not supported in older kubernetes clusters
|
||||
4. Bloated ConfigMap - cannot be paginated and will grow very big on DNS provider with thousands of records
|
||||
|
||||
#### Failure scenario
|
||||
|
||||
It is possible that the configmap will go out of sync with the dns provider state. In the implementation flow external-dns will first modify records on dns provider side to subsequently update configmap. And if ExternalDNS will crash in-between two operation created records will be left unmanaged and not viable for update/deletion by External DNS.
|
||||
|
||||
## Component integration
|
||||
|
||||
Components:
|
||||
* Source - all endpoints ( collection of ingress, service[type=LoadBalancer] etc.)
|
||||
* [Plan](https://github.com/kubernetes-sigs/external-dns/issues/13) - object responsible for the create of change lists in external-dns
|
||||
* Provider - interface to access the DNS provider API
|
||||
|
||||
Registry will serve as wrapper around `Provider` providing additional information regarding endpoint ownership. Ownership will further taken into account by `Plan` to filter out records to include only records managed by current ExternalDNS instance (having same `owner-id` value)
|
||||
|
||||
A single loop iteration of external-dns operation:
|
||||
|
||||
1. Get all endpoints ( collection ingress, service[type=LoadBalancer] etc.) into collection of `endpoints`
|
||||
2. Get registry `Records()` (makes the call to DNSProvider and also build ownership information)
|
||||
3. Pass `Records` (including ownership information) and list of endpoints to `Plan` to do the calculation
|
||||
4. Call registry `ApplyChanges()` method (which subsequently calls DNS Provider Apply method to update records)
|
||||
5. If ConfigMap implementation of Registry is used, then ConfigMap needs to be updated separately
|
||||
|
||||
~~In case of configmap, Registry gets updated all the time via `Poll`. `Plan` does not call DNS provider directly. Good value of the `Poll` is to have simple rate limiting mechanism on DNS provider.~~
|
||||
|
||||
#### Notes:
|
||||
|
||||
1. DNS Provider should use batch operations
|
||||
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
|
||||
|
||||
|
@ -1,80 +0,0 @@
|
||||
### TXT Registry migration to a new format ###
|
||||
|
||||
In order to support more record types and be able to track ownership without TXT record name clash, a new TXT record is introduced.
|
||||
It contains record type it manages, e.g.:
|
||||
* A record foo.example.com will be tracked with classic foo.example.com TXT record
|
||||
* At the same time a new TXT record will be created a-foo.example.com
|
||||
|
||||
Prefix and suffix are extended with %{record_type} template where the user can control how prefixed/suffixed records should look like.
|
||||
|
||||
In order to maintain compatibility, both records will be maintained for some time, in order to have downgrade possibility.
|
||||
The controller will try to create the "new format" TXT records if they are not present to ease the migration from the versions < 0.12.0.
|
||||
|
||||
Later on, the old format will be dropped and only the new format will be kept (<record_type>-<endpoint_name>).
|
||||
|
||||
Cleanup will be done by controller itself.
|
||||
|
||||
### Encryption of TXT Records
|
||||
TXT records may contain sensitive information, such as the internal ingress name or namespace, which attackers could exploit to gather information about your infrastructure.
|
||||
By encrypting TXT records, you can protect this information from unauthorized access. It is strongly recommended to encrypt all TXT records to prevent potential security breaches.
|
||||
|
||||
To enable encryption of TXT records, you can use the following parameters:
|
||||
- `--txt-encrypt-enabled=true`
|
||||
- `--txt-encrypt-aes-key=32bytesKey` (used for AES-256-GCM encryption and should be exactly 32 bytes)
|
||||
|
||||
Note that the key used for encryption should be a secure key and properly managed to ensure the security of your TXT records.
|
||||
|
||||
### Generating TXT encryption AES key
|
||||
Python
|
||||
```python
|
||||
python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
|
||||
```
|
||||
|
||||
Bash
|
||||
```shell
|
||||
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo
|
||||
```
|
||||
|
||||
OpenSSL
|
||||
```shell
|
||||
openssl rand -base64 32 | tr -- '+/' '-_'
|
||||
```
|
||||
|
||||
PowerShell
|
||||
```powershell
|
||||
# Add System.Web assembly to session, just in case
|
||||
Add-Type -AssemblyName System.Web
|
||||
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Web.Security.Membership]::GeneratePassword(32,4))).Replace("+","-").Replace("/","_")
|
||||
```
|
||||
|
||||
Terraform
|
||||
```hcl
|
||||
resource "random_password" "txt_key" {
|
||||
length = 32
|
||||
override_special = "-_"
|
||||
}
|
||||
```
|
||||
|
||||
### Manually Encrypt/Decrypt TXT Records
|
||||
|
||||
In some cases, you may need to edit labels generated by External-DNS, and in such cases, you can use simple Golang code to do that.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func main() {
|
||||
key := []byte("testtesttesttesttesttesttesttest")
|
||||
encrypted, _ := endpoint.EncryptText(
|
||||
"heritage=external-dns,external-dns/owner=example,external-dns/resource=ingress/default/example",
|
||||
key,
|
||||
nil,
|
||||
)
|
||||
decrypted, _, _ := endpoint.DecryptText(encrypted, key)
|
||||
fmt.Println(decrypted)
|
||||
}
|
||||
```
|
16
docs/registry/registry.md
Normal file
16
docs/registry/registry.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Registries
|
||||
|
||||
A registry persists metadata pertaining to DNS records.
|
||||
|
||||
The most important metadata is the owning external-dns deployment.
|
||||
This is specified using the `--txt-owner-id` flag, specifying a value unique to the
|
||||
deployment of external-dns and which doesn't change for the lifetime of the deployment.
|
||||
Deployments in different clusters but sharing a DNS zone need to use different owner IDs.
|
||||
|
||||
The registry implementation is specified using the `--registry` flag.
|
||||
|
||||
## Supported registries
|
||||
|
||||
* [txt](txt.md) (default) - Stores in TXT records in the same provider
|
||||
* noop - Passes metadata directly to the provider. For most providers, this means the metadata is not persisted.
|
||||
* aws-sd - Stores metadata in AWS Service Discovery. Only usable with the `aws-sd` provider.
|
97
docs/registry/txt.md
Normal file
97
docs/registry/txt.md
Normal file
@ -0,0 +1,97 @@
|
||||
# The TXT registry
|
||||
|
||||
The TXT registry is the default registry.
|
||||
It stores DNS record metadata in TXT records, using the same provider.
|
||||
|
||||
## Prefixes and Suffixes
|
||||
|
||||
In order to avoid having the registry TXT records collide with
|
||||
TXT or CNAME records created from sources, you can configure a fixed prefix or suffix
|
||||
to be added to the first component of the domain of all registry TXT records.
|
||||
|
||||
The prefix or suffix may not be changed after initial deployment,
|
||||
lest the registry records be orphaned and the metadata be lost.
|
||||
|
||||
The prefix or suffix may contain the substring `%{record_type}`, which is replaced with
|
||||
the record type of the DNS record for which it is storing metadata.
|
||||
|
||||
The prefix is specified using the `--txt-prefix` flag and the suffix is specified using
|
||||
the `--txt-suffix` flag. The two flags are mutually exclusive.
|
||||
|
||||
## Wildcard Replacement
|
||||
|
||||
The `--txt-wildcard-replacement` flag specifies a string to use to replace the "*" in
|
||||
registry TXT records for wildcard domains. Without using this, registry TXT records for
|
||||
wildcard domains will have invalid domain syntax and be rejected by most providers.
|
||||
|
||||
## Encryption
|
||||
|
||||
Registry TXT records may contain information, such as the internal ingress name or namespace, considered sensitive, , which attackers could exploit to gather information about your infrastructure.
|
||||
By encrypting TXT records, you can protect this information from unauthorized access.
|
||||
|
||||
Encryption is enabled by using the `--txt-encrypt-enabled` flag. The 32-byte AES-256-GCM encryption
|
||||
key must be specified in URL-safe base64 form, using the `--txt-encrypt-aes-key` flag.
|
||||
|
||||
Note that the key used for encryption should be a secure key and properly managed to ensure the security of your TXT records.
|
||||
|
||||
### Generating the TXT Encryption Key
|
||||
Python
|
||||
```python
|
||||
python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
|
||||
```
|
||||
|
||||
Bash
|
||||
```shell
|
||||
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo
|
||||
```
|
||||
|
||||
OpenSSL
|
||||
```shell
|
||||
openssl rand -base64 32 | tr -- '+/' '-_'
|
||||
```
|
||||
|
||||
PowerShell
|
||||
```powershell
|
||||
# Add System.Web assembly to session, just in case
|
||||
Add-Type -AssemblyName System.Web
|
||||
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Web.Security.Membership]::GeneratePassword(32,4))).Replace("+","-").Replace("/","_")
|
||||
```
|
||||
|
||||
Terraform
|
||||
```hcl
|
||||
resource "random_password" "txt_key" {
|
||||
length = 32
|
||||
override_special = "-_"
|
||||
}
|
||||
```
|
||||
|
||||
### Manually Encrypting/Decrypting TXT Records
|
||||
|
||||
In some cases you might need to edit registry TXT records. The following example Go code encrypts and decrypts such records.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func main() {
|
||||
key := []byte("testtesttesttesttesttesttesttest")
|
||||
encrypted, _ := endpoint.EncryptText(
|
||||
"heritage=external-dns,external-dns/owner=example,external-dns/resource=ingress/default/example",
|
||||
key,
|
||||
nil,
|
||||
)
|
||||
decrypted, _, _ := endpoint.DecryptText(encrypted, key)
|
||||
fmt.Println(decrypted)
|
||||
}
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
The TXT registry can optionally cache DNS records read from the provider. This can mitigate
|
||||
rate limits imposed by the provider.
|
||||
|
||||
Caching is enabled by specifying a cache duration with the `--txt-cache-interval` flag.
|
13
mkdocs.yml
13
mkdocs.yml
@ -6,7 +6,15 @@ repo_url: https://github.com/kubernetes-sigs/external-dns/
|
||||
|
||||
nav:
|
||||
- index.md
|
||||
- About:
|
||||
- FAQ: faq.md
|
||||
- Out of Incubator: 20190708-external-dns-incubator.md
|
||||
- Code of Conduct: code-of-conduct.md
|
||||
- License: LICENSE.md
|
||||
- Tutorials: tutorials/
|
||||
- Registries:
|
||||
- About: registry/registry.md
|
||||
- TXT: registry/txt.md
|
||||
- Advanced Topics:
|
||||
- Initial Design: initial-design.md
|
||||
- TTL: ttl.md
|
||||
@ -14,11 +22,6 @@ nav:
|
||||
- Kubernetes Contributions: CONTRIBUTING.md
|
||||
- Release: release.md
|
||||
- contributing/*
|
||||
- About:
|
||||
- FAQ: faq.md
|
||||
- Out of Incubator: 20190708-external-dns-incubator.md
|
||||
- Code of Conduct: code-of-conduct.md
|
||||
- License: LICENSE.md
|
||||
|
||||
theme:
|
||||
name: material
|
||||
|
Loading…
Reference in New Issue
Block a user