Skip to content

Commit

Permalink
Merge 5543b71 into 5fc6adf
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugome committed Feb 25, 2020
2 parents 5fc6adf + 5543b71 commit 71336e1
Show file tree
Hide file tree
Showing 9 changed files with 921 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ ExternalDNS' current release is `v0.6`. This version allows you to keep selected
* [NS1](https://ns1.com/)
* [TransIP](https://www.transip.eu/domain-name/)
* [VinylDNS](https://www.vinyldns.io)
* [OVH](https://www.ovh.com)

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.

Expand Down Expand Up @@ -93,6 +94,7 @@ The following table clarifies the current status of the providers according to t
| VinylDNS | Alpha |
| RancherDNS | Alpha |
| Akamai FastDNS | Alpha |
| OVH | Alpha |

## Running ExternalDNS:

Expand Down Expand Up @@ -138,6 +140,7 @@ The following tutorials are provided:
* [RFC2136](docs/tutorials/rfc2136.md)
* [TransIP](docs/tutorials/transip.md)
* [VinylDNS](docs/tutorials/vinyldns.md)
* [OVH](docs/tutorials/ovh.md)

### Running Locally

Expand Down Expand Up @@ -268,6 +271,7 @@ Here's a rough outline on what is to come (subject to change):
### v0.6

- [ ] Ability to replace Kops' [DNS Controller](https://github.com/kubernetes/kops/tree/master/dns-controller) (This could also directly become `v1.0`)
- [x] Support for OVH

### v1.0

Expand Down
251 changes: 251 additions & 0 deletions docs/tutorials/ovh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
# Setting up ExternalDNS for Services on OVH

This tutorial describes how to setup ExternalDNS for use within a
Kubernetes cluster using OVH DNS.

Make sure to use **>=0.6** version of ExternalDNS for this tutorial.

## Creating a zone with OVH DNS

If you are new to OVH, we recommend you first read the following
instructions for creating a zone.

[Creating a zone using the OVH manager](https://docs.ovh.com/gb/en/domains/create_a_dns_zone_for_a_domain_which_is_not_registered_at_ovh/)

[Creating a zone using the OVH API](https://api.ovh.com/console/)

## Creating OVH Credentials

You first need to create an OVH application.

Using the [OVH documentation](https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/#creation-of-your-application-keys) you will have your `Application key` and `Application secret`

And you will need a `Consumer key`, you can ask `External DNS` to generate that for you, using the `--ovh-generate-consumer` option.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --provider=ovh
- --ovh-generate-consumer
env:
- name: OVH_APPLICATION_KEY
value: "YOUR_OVH_APPLICATION_KEY"
- name: OVH_APPLICATION_SECRET
value: "YOUR_OVH_APPLICATION_SECRET"
```

In log, you will have two log lines :
```
INFO[0000] Generated consumer key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
INFO[0000] Please visit https://eu.api.ovh.com/auth/?credentialToken=YYYYYYYYYYYYYY to validate it
```

Keep the `Consumer key` and go to the link to allow external dns to manage your OVH Dns zone.

You can also generate the key manually, here the access needed :
- GET on `/domain/zone`
- GET on `/domain/zone/*/record`
- POST on `/domain/zone/*/record`
- PUT on `/domain/zone/*/record`
- DELETE on `/domain/zone/*/record`
- GET on `/domain/zone/*/record/*`
- POST on `/domain/zone/*/record/*`
- PUT on `/domain/zone/*/record/*`
- DELETE on `/domain/zone/*/record/*`
- POST on `/domain/zone/*/refresh`

## Deploy ExternalDNS

Connect your `kubectl` client to the cluster with which you want to test ExternalDNS, and then apply one of the following manifest files for deployment:

### Manifest (for clusters without RBAC enabled)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
- --provider=ovh
env:
- name: OVH_APPLICATION_KEY
value: "YOUR_OVH_APPLICATION_KEY"
- name: OVH_APPLICATION_SECRET
value: "YOUR_OVH_APPLICATION_SECRET"
- name: OVH_CONSUMER_KEY
value: "YOUR_OVH_CONSUMER_KEY_AFTER_VALIDATED_LINK"
```

### Manifest (for clusters with RBAC enabled)

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:latest
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
- --provider=ovh
env:
- name: OVH_APPLICATION_KEY
value: "YOUR_OVH_APPLICATION_KEY"
- name: OVH_APPLICATION_SECRET
value: "YOUR_OVH_APPLICATION_SECRET"
- name: OVH_CONSUMER_KEY
value: "YOUR_OVH_CONSUMER_KEY_AFTER_VALIDATED_LINK"
```

## Deploying an Nginx Service

Create a service file called 'nginx.yaml' with the following contents:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: example.com
external-dns.alpha.kubernetes.io/ttl: "120" #optional
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
```

**A note about annotations**

Verify that the annotation on the service uses the same hostname as the OVH DNS zone created above. The annotation may also be a subdomain of the DNS zone (e.g. 'www.example.com').

The TTL annotation can be used to configure the TTL on DNS records managed by ExternalDNS and is optional. If this annotation is not set, the TTL on records managed by ExternalDNS will default to 10.

ExternalDNS uses the hostname annotation to determine which services should be registered with DNS. Removing the hostname annotation will cause ExternalDNS to remove the corresponding DNS records.

### Create the deployment and service

```
$ kubectl create -f nginx.yaml
```

Depending on where you run your service, it may take some time for your cloud provider to create an external IP for the service. Once an external IP is assigned, ExternalDNS detects the new service IP address and synchronizes the OVH DNS records.

## Verifying OVH DNS records

Use the OVH manager or API to verify that the A record for your domain shows the external IP address of the services.

## Cleanup

Once you successfully configure and verify record management via ExternalDNS, you can delete the tutorial's example:

```
$ kubectl delete -f nginx.yaml
$ kubectl delete -f externaldns.yaml
```
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
github.com/go-resty/resty v1.8.0 // indirect
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b // indirect
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
github.com/gophercloud/gophercloud v0.1.0
github.com/heptio/contour v0.15.0
github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65
Expand All @@ -37,6 +38,7 @@ require (
github.com/nesv/go-dynect v0.6.0
github.com/nic-at/rc0go v1.1.0
github.com/oracle/oci-go-sdk v1.8.0
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.3
github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ github.com/operator-framework/operator-sdk v0.7.0/go.mod h1:iVyukRkam5JZa8AnjYf+
github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs=
github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
Expand Down Expand Up @@ -740,6 +742,7 @@ gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdOD
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/logfmt.v0 v0.3.0/go.mod h1:mRLMcMLrml5h2Ux/H+4zccFOlVCiRvOvndsolsJoU8Q=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ func main() {
p, err = provider.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.DryRun)
case "digitalocean":
p, err = provider.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun)
case "ovh":
p, err = provider.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHGenerateConsumerKey, cfg.DryRun)
case "linode":
p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version)
case "dnsimple":
Expand Down
8 changes: 7 additions & 1 deletion pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ type Config struct {
DynMinTTLSeconds int
OCIConfigFile string
InMemoryZones []string
OVHEndpoint string
OVHGenerateConsumerKey bool
PDNSServer string
PDNSAPIKey string `secure:"yes"`
PDNSTLSEnabled bool
Expand Down Expand Up @@ -189,6 +191,8 @@ var defaultConfig = &Config{
InfobloxMaxResults: 0,
OCIConfigFile: "/etc/kubernetes/oci.yaml",
InMemoryZones: []string{},
OVHEndpoint: "ovh-eu",
OVHGenerateConsumerKey: false,
PDNSServer: "http://localhost:8081",
PDNSAPIKey: "",
PDNSTLSEnabled: false,
Expand Down Expand Up @@ -301,7 +305,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)

// Flags related to providers
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
Expand Down Expand Up @@ -344,6 +348,8 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("oci-config-file", "When using the OCI provider, specify the OCI configuration file (required when --provider=oci").Default(defaultConfig.OCIConfigFile).StringVar(&cfg.OCIConfigFile)
app.Flag("rcodezero-txt-encrypt", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").Default(strconv.FormatBool(defaultConfig.RcodezeroTXTEncrypt)).BoolVar(&cfg.RcodezeroTXTEncrypt)
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)
app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint)
app.Flag("ovh-generate-consumer", "When using the OVH provider, will generate a consumer and validation URL (default: false)").Default(strconv.FormatBool(defaultConfig.OVHGenerateConsumerKey)).BoolVar(&cfg.OVHGenerateConsumerKey)
app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer)
app.Flag("pdns-api-key", "When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns)").Default(defaultConfig.PDNSAPIKey).StringVar(&cfg.PDNSAPIKey)
app.Flag("pdns-tls-enabled", "When using the PowerDNS/PDNS provider, specify whether to use TLS (default: false, requires --tls-ca, optionally specify --tls-client-cert and --tls-client-cert-key)").Default(strconv.FormatBool(defaultConfig.PDNSTLSEnabled)).BoolVar(&cfg.PDNSTLSEnabled)
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/externaldns/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ var (
InfobloxMaxResults: 0,
OCIConfigFile: "/etc/kubernetes/oci.yaml",
InMemoryZones: []string{""},
OVHEndpoint: "ovh-eu",
OVHGenerateConsumerKey: false,
PDNSServer: "http://localhost:8081",
PDNSAPIKey: "",
Policy: "sync",
Expand Down Expand Up @@ -144,6 +146,8 @@ var (
InfobloxMaxResults: 2000,
OCIConfigFile: "oci.yaml",
InMemoryZones: []string{"example.org", "company.com"},
OVHEndpoint: "ovh-ca",
OVHGenerateConsumerKey: true,
PDNSServer: "http://ns.example.com:8081",
PDNSAPIKey: "some-secret-key",
PDNSTLSEnabled: true,
Expand Down Expand Up @@ -229,6 +233,8 @@ func TestParseFlags(t *testing.T) {
"--infoblox-max-results=2000",
"--inmemory-zone=example.org",
"--inmemory-zone=company.com",
"--ovh-endpoint=ovh-ca",
"--ovh-generate-consumer",
"--pdns-server=http://ns.example.com:8081",
"--pdns-api-key=some-secret-key",
"--pdns-tls-enabled",
Expand Down Expand Up @@ -315,6 +321,8 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000",
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
"EXTERNAL_DNS_OVH_ENDPOINT": "ovh-ca",
"EXTERNAL_DNS_OVH_GENERATE_CONSUMER": "true",
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
"EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com",
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
Expand Down
Loading

0 comments on commit 71336e1

Please sign in to comment.