Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/{k8s-operator,k8s-nameserver},k8s-operator: update nameserver config with records for ingress/egress proxies #11019

Merged
merged 2 commits into from
May 2, 2024

Conversation

irbekrm
Copy link
Contributor

@irbekrm irbekrm commented Feb 2, 2024

This PR adds functionality to the operator to optionally populate DNS records config for the in-cluster ts.net nameserver (added in #11017) with records for cluster proxies' MagicDNS names:

  • For Tailscale Ingress the record is a mapping between Ingresss MagicDNS name and the proxy's Pod IP

  • For cluster egress proxies configured via tailscale.com/tailnet-fqdn annotation the record is a mapping between the annotation's value and the proxy's Pod IP

No records are currently created for any other proxy types.

The records are created only if there is a ready tailscale.com/v1alpha1.DNSConfig instance in the cluster (see #11017 ) i.e if users have explicitly configured the operator to deploy the in-cluster ts.net nameserver.

Below see the user flow for a setup where:

  • cluster A runs a service exposed over Tailscale Ingress that should be accesible by callers on tailnet as well as cluster workloads via the same DNS name (the MagicDNS of the Ingress) (see use case description).
    cluster A uses kube-dns.
  • cluster B's workloads also need to access the service in cluster A and would like to do that using the same Ingress. They can do it by exposing the Ingress to their cluster (see cross-cluster connectivity). Because A's Service is exposed over HTTPS, they need to use the MagicDNS name of the Ingress (so it needs to resolve within the cluster). See a use case.
    cluster B uses CoreDNS.
In cluster A:
  1. Deploy the operator
  2. Create a DNSConfig to tell the operator to deploy an in-cluster ts.net nameserver, i.e
$ kubectl apply -f ./cmd/k8s-operator/examples/dnsconfig.yaml
  1. Update kube-dns ConfigMap to tell kube-dns to use the ts.net nameserver to resolve ts.net DNS names
$ k get dnsconfig NAME NAMESERVERIP ts-dns 10.27.1.90 $ kubectl get cm kube-dns -n kube-system -oyaml apiVersion: v1 data: stubDomains: | { "ts.net": ["10.27.1.90"] } kind: ConfigMap metadata: name: kube-dns namespace: kube-system ...
  1. Create a workload exposed via tailscale Ingress.
    Note the use of tailscale.com/experimental-forward-cluster-traffic-via-ingress annotation- this makes the ingress proxy to listen on the Pod's IP address which it does not do by default.
apiVersion: apps/v1 kind: Deployment metadata: name: kuardfoobar spec: selector: matchLabels: app: kuardfoobar template: metadata: labels: app: kuardfoobar spec: containers: - image: gcr.io/kuar-demo/kuard-amd64:1 name: kuardfoobar ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: kuardfoobar spec: ports: - port: 80 targetPort: 8080 type: ClusterIP selector: app: kuardfoobar --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kuardfoobar annotations: tailscale.com/experimental-forward-cluster-traffic-via-ingress: "true" spec: tls: - hosts: - "dnstest" rules: - http: paths: - backend: service: name: kuardfoobar port: number: 80 pathType: Prefix path: / ingressClassName: tailscale
  1. Observe the MagicDNS name of the Ingress and that the workload can be accessed over that from a non-tailscale Pod in cluster:
$ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE kuardfoobar tailscale * dnstest-3.tailbd97a.ts.net 80, 443 64m $ kubectl exec -it somepod -- sh / # curl -vvv https://dnstest-3.tailbd97a.ts.net * Host dnstest-3.tailbd97a.ts.net:443 was resolved. * IPv6: (none) * IPv4: 10.0.0.6 * Trying 10.0.0.6:443... * Connected to dnstest-3.tailbd97a.ts.net (10.0.0.6) port 443 ....

In cluster B:

  1. Deploy the operator
  2. Create a DNSConfig to tell the operator to deploy an in-cluster ts.net nameserver, i.e
$ kubectl apply -f ./cmd/k8s-operator/examples/dnsconfig.yaml
  1. Update CoreDNS config to tell it to use the ts.net nameserver to resolve ts.net DNS names:
$ kubectl get dnsconfig NAME NAMESERVERIP ts-dns 10.100.198.212 $ kubectl get cm coredns -n kube-system -oyaml apiVersion: v1 data: Corefile: | ts.net { cache 5 errors forward . 10.100.198.212 } .:53 { } kind: ConfigMap metadata: name: coredns namespace: kube-system
  1. Create a tailscale egress proxy with a tailscale.com/tailnet-fqdn annotation pointing at the Ingress in cluster A:
kind: Service metadata: annotations: tailscale.com/tailnet-fqdn: dnstest-3.tailbd97a.ts.net name: kuard-egress spec: type: ExternalName
  1. Verify that you can access the workload in cluster A over the MagicDNS name of Ingress in cluster A from a non-tailscale Pod in cluster B:
$ kubectl exec -it sompod -- sh # curl -vvv https://dnstest-3.tailbd97a.ts.net * Host dnstest-3.tailbd97a.ts.net:443 was resolved. * IPv6: (none) * IPv4: 172.31.24.228 * Trying 172.31.24.228:443... * Connected to dnstest-3.tailbd97a.ts.net (172.31.24.228) port 443 ...

Notes:

  • users have to manually update kube-dns/CoreDNS config to add the stub nameserver's Service IP
  • IPv6 Pod addresses currently not supported
  • To access Tailscale Ingress from within a cluster via its MagicDNS name, users also have to annotate the Ingress resource with tailscale.com/experimental-forward-cluster-traffic-via-ingress annotation, see
    cmd/{containerboot,k8s-operator/deploy/manifests}: optionally allow proxying cluster traffic to a cluster target via ingress proxy #11036
  • I have also tested that non-operator managed tailscale instances that accept Tailscale DNS, running in a Pod in a cluster configured with our nameserver, still use 100.100.100.100 to resolve tailnet DNS names. Operator configured proxies do not accept Tailscale DNS, so they would use the ts.net nameserver. I cannot think of cases where this would be an issue (because they don't need to talk to anything on tailnet via MagicDNS)

cc @jaxxstorm

Next steps:

  • update build infra to publish nameserver images

@irbekrm irbekrm marked this pull request as draft February 2, 2024 19:29
@irbekrm irbekrm changed the title Irbekrm/kubednsrecords cmd/k8s-operator: update nameserver config with records for ingress/egress proxies Feb 2, 2024
@irbekrm irbekrm force-pushed the irbekrm/kubednsrecords branch 4 times, most recently from 15ec9ad to 741167e Compare February 2, 2024 20:01
return svc != nil && svc.Annotations[AnnotationExpose] == "true"
}

// hasTailnetTargetAnnotation returns the value of tailscale.com/tailnet-ip
// annotation or of the deprecated tailscale.com/ts-tailnet-target-ip
// annotation. If neither is set, it returns an empty string. If both are set,
// it returns the value of the new annotation.
func (a *ServiceReconciler) tailnetTargetAnnotation(svc *corev1.Service) string {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes make these functions re-usable for dns-records reconciler.

nameserver:
image:
repo: gcr.io/csi-test-290908/nameserver
tag: v0.0.1ns
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add functionality to actually push the nameserver image in a follow-up PR

Comment on lines 39 to 44
// The records that it creates are:
// - For tailscale Ingress, a mapping of the Ingress's MagicDNSName to the IP address of
// the ingress proxy Pod.
// - For egress proxies configured via tailscale.com/tailnet-fqdn annotation, a
// mapping of the the tailnet FQDN to the IP address of the egress proxy Pod.
//
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I was creating records for all proxy types. However, the L3 ingress proxies don't route cluster traffic to the backend. Also no-one has asked for this functionality for cluster ingress proxies and with regards to the egress proxies, it seems good enough to only implement it for the ones configured via tailscale.com/tailnet-fqdn annotation and not tailscale.com/tailnet-ip. We can always add more functionality later.

// egress configured via tailscale.com/tailnet-fqdn annotation.
//
// For Ingress, the record is a mapping between the MagicDNSName of the Ingress, retrieved from
// ingress.status.loadBalancer.ingress.hostname field and the proxy Pod IP addresses
Copy link
Contributor Author

@irbekrm irbekrm Apr 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently always a single IP address

@irbekrm irbekrm force-pushed the irbekrm/kubednsrecords branch 2 times, most recently from 1567e91 to 7c266d6 Compare April 4, 2024 14:54
ips := make([]string, 0)
for _, ep := range eps.Endpoints {
for _, ip := range ips {
if !net.IsIPv4String(ip) {
Copy link
Contributor Author

@irbekrm irbekrm Apr 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add IPv6 support in a follow-up. At the moment the nameserver only responds to queries for A records.

@irbekrm irbekrm force-pushed the irbekrm/kubednsrecords branch 4 times, most recently from be183ff to dd28a4a Compare April 4, 2024 15:17
@irbekrm irbekrm changed the title cmd/k8s-operator: update nameserver config with records for ingress/egress proxies cmd/{k8s-operator,k8s-nameserver},k8s-operator: update nameserver config with records for ingress/egress proxies Apr 4, 2024
@irbekrm irbekrm marked this pull request as draft April 9, 2024 06:28
@Scalahansolo
Copy link

What's the status of this? did this PR get abandoned in favor of a different approach?

@irbekrm
Copy link
Contributor Author

irbekrm commented Apr 29, 2024

Hi @Scalahansolo , it's not abandoned, just that we're a bit low bandwidth right now. The current plan is still to get it in.

…ords for proxies.

This commit adds functionality to automatically populate
DNS records for the in-cluster ts.net nameserver
to allow cluster workloads to resolve MagicDNS names
associated with operator's proxies.

The records are created as follows:
* For tailscale Ingress proxies there will be
a record mapping the MagicDNS name of the Ingress
device and each proxy Pod's IP address.
* For cluster egress proxies, configured via
tailscale.com/tailnet-fqdn annotation, there will be
a record for each proxy Pod, mapping
the MagicDNS name of the exposed
tailnet workload to the proxy Pod's IP.

No records will be created for any other proxy types.
Records will only be created if users have configured
the operator to deploy an in-cluster ts.net nameserver
by applying tailscale.com/v1alpha1.DNSConfig.

It is user's responsibility to add the ts.net nameserver
as a stub nameserver for ts.net DNS names.
https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#configuration-of-stub-domain-and-upstream-nameserver-using-coredns
https://cloud.google.com/kubernetes-engine/docs/how-to/kube-dns#upstream_nameservers

See also #11017

Updates #10499

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
@irbekrm irbekrm marked this pull request as ready for review April 30, 2024 19:22
Copy link
Contributor

@oxtoacart oxtoacart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM, though I'm not familiar with the K8s APIs being used.

I left a few comments/questions.

cmd/k8s-operator/dnsrecords.go Outdated Show resolved Hide resolved
cmd/k8s-operator/dnsrecords.go Show resolved Hide resolved
cmd/k8s-operator/dnsrecords.go Show resolved Hide resolved
cmd/k8s-operator/dnsrecords.go Show resolved Hide resolved
cmd/k8s-operator/dnsrecords.go Outdated Show resolved Hide resolved
@@ -60,7 +60,7 @@ type DNSConfigStatus struct {
// +optional
Conditions []ConnectorCondition `json:"conditions"`
// +optional
NameserverStatus *NameserverStatus `json:"nameserverStatus"`
Nameserver *NameserverStatus `json:"nameserver"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this to not have dnsconfig.status.nameserverStatus

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
@irbekrm irbekrm merged commit 19b31ac into main May 2, 2024
49 checks passed
@irbekrm irbekrm deleted the irbekrm/kubednsrecords branch May 2, 2024 16:29
irbekrm added a commit that referenced this pull request May 2, 2024
Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request May 2, 2024
Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request May 17, 2024
…NSConfig description

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request Jun 7, 2024
…NSConfig description

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request Jun 7, 2024
…NSConfig description

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request Jun 7, 2024
…NSConfig description

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request Jun 7, 2024
…NSConfig description

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
irbekrm added a commit that referenced this pull request Jun 7, 2024
…NSConfig description (#11971)

Also removes hardcoded image repo/tag from example DNSConfig resource
as the operator now knows how to default those.

Updates #11019

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants