traefik

package module
v0.0.0-...-eaf2003 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2024 License: Apache-2.0 Imports: 23 Imported by: 0

README

coredns traefik plugin

Name

traefik - returns CNAMEs for hosts registered in Traefik instances

Description

Extracts FQDN's from the Host() and HostSNI() values in traefik's http router rules (Traefik's /api/http/routers endpoint). Returns a CNAME result with the traefik instance's domain.

Why?

Homelab + free time. Prior to using traefik, I had a few nginx containers running in different vlans in my homelab. I would create config files by hand, setup static DNS entries, generate certs, and reverse proxy to docker containers by hand.

Traefik took care of a lot of that for me, but I still needed to setup the static DNS entries. Basically I would have a single IP for traefik and a bunch of CNAMEs to it for the various containerized services. Rather than go into pfSense to do this every time, I figured I'd find something more automatic. So I spun up CoreDNS and threw this plugin together to poll the Traefik API periodically and figure out what host names I have http routers referring to. For each of those, I can respond with a CNAME to the Traefik server on-the-fly.

When you pair this with Traefik's ability to configure routes and services via Docker labels, this completely automates the DNS, TLS, and reverse proxy pain when spinning up new containers. It's a nice stopgap until I get around to making the switch to kubernetes & should play nicely during the transition period.

Compilation

This package will always be compiled as part of CoreDNS and not in a standalone way. It will require you to use go get or as a dependency on plugin.cfg.

The manual will have more information about how to configure and extend the server with external plugins.

A simple way to consume this plugin, is by adding the following on plugin.cfg, and recompile it as detailed on coredns.io.

traefik:github.com/scottt732/coredns-traefik

Put this early in the plugin list, so that traefik is executed before any of the other plugins.

After this you can compile coredns by:

go generate
go build

Or you can instead use make:

make

Settings

traefik https://your-traefik.homelab.net/api {
  cname your-traefik.homelab.net
  a 10.0.0.2
  refreshinterval 30
  ttl 30
  insecureskipverify [true|false*]
  resolveapihost [true|false*]
}
  • https://your-traefik.homelab.net/api - This is the endpoint that your Traefik API is listening on. You may need to configure Traefik to expose this.
  • Either (but not both):
    • cname - The host name to return for HTTP routes defined in Traefik. Typically, this is the same host name as in the API URI above.
    • a - The IP address to return for HTTP routes defined in Traefik. Typically, this is the IP address of the Traefik server.
  • refreshinterval - How frequently to poll for changes in Traefik
  • ttl - How long should the results be persisted in downstream DNS caches
  • insecureskipverify - Optional. if your Traefik API endpoint requires HTTPS but you don't have a valid/trusted certificate
  • resolveapihost - Optional. When true and you've chosen a above, this will cause the plugin to resolve your-traefik.homelab.net itself to the IP address specified in the a block
  • fallthrough - Optional

Syntax

Option 1: Returning CNAMEs
traefik https://your-traefik.homelab.net/api {
  cname your-traefik.homelab.net
  refreshinterval 30
  ttl 30
}
  • https://your-traefik.homelab.net/api refers to the base Traefik API endpoint (without trailing slash). Given the base URL, this will hit https://your-traefik.homelab.net/api/http/routers endpoint.
  • cname is the fully qualified domain name (with or without trailing .) that matching requests will have returned. Usually this will be the host name of the API endpoint above.
  • refreshinterval specifies how frequently that api/http/routers endpoint is polled for changes (in seconds)
  • ttl determines that time-to-live for successful responses (in seconds).
Option 2: Returning A records
traefik https://your-traefik.homelab.net/api {
  a 10.0.0.2
  refreshinterval 30
  ttl 30
}
  • a indicates the IP address(es) of your-traefik.homelab.net. This IP address will be returned as A records for all HTTP Routers discovered in Traefik.
  • You can optionally specify a resolveapihost [true|false] (optional, default=false) to have this plugin return A 10.0.0.2 for your-traefik.homelab.net lookups
Example

Imagine you have services in your homelab which you'd like to expose internally/externally as homelab.net subdomains.

  • Traefik is listening on 80/443 on a host whose IP is 10.10.10.2
  • The primary gateway/DNS is on 10.10.10.1
  • You want traefik.homelab.net to route to Traefik's dashboard/API.
  • You want a container named gitea to be resolvable as mytestservice.homelab.net with Traefik taking care of TLS/acme and reverse proxying.

Given this CoreDNS corefile fragment:

homelab.net:53 {
    hosts {
        10.10.10.2 traefik.homelab.net
        fallthrough
    }
    traefik https://traefik.homelab.net/api {
        cname traefik.homelab.net
        refreshinterval 30
        ttl 5
    }
    forward . dns://10.10.10.1
}

This will cause requests for traefik.homelab.net to return the A record 10.10.0.2.

Then we can use a compose.yml like this.

...
services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gitea-web.rule=Host(`gitea.homelab.net`)"
      - "traefik.http.routers.gitea-web.service=gitea-web-svc"
      - "traefik.http.routers.gitea-web.entrypoints=websecure"
      - "traefik.http.routers.gitea-web.tls=true"
      - "traefik.http.routers.gitea-web.tls.domains[0].main=homelab.net"
      - "traefik.http.routers.gitea-web.tls.domains[0].sans=*.homelab.net"
      - "traefik.http.services.gitea-web-svc.loadbalancer.server.scheme=http"
      - "traefik.http.services.gitea-web-svc.loadbalancer.server.port=3000"
    ...

The container labels cause Traefik's routers & services to get wired together, waiting for a match on gitea.homelab.net.

This plugin polls the Traefik API and discovers gitea.homelab.net. It dynamically returns a CNAME to traefik.homelab.net, which gets resolved via the A record to 10.10.10.2. Traefik receives the request and sees the host name matches its rule.

In my case, pfSense's DNS Resolver handles DNS for my network and I setup a conditional forwarder for the zone homelab.net to point just those requests to the CoreDNS instance. Alternatively, you can send all of your DNS requests through CoreDNS.

Metrics

If monitoring is enabled (via the prometheus directive) the following metric is exported:

  • coredns_traefik_request_count_total{server} - query count to the traefik plugin.

The server label indicated which server handled the request, see the metrics plugin for details.

Ready

This plugin reports readiness to the ready plugin. It will be ready after the first refresh of data

Also See

See the manual.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertIPsToString

func ConvertIPsToString(ips []net.IP) string

Types

type HttpRouter

type HttpRouter struct {
	EntryPoints []string       `json:"entryPoints"`
	Service     string         `json:"service"`
	Rule        string         `json:"rule"`
	Priority    int            `json:"priority"`
	Tls         *HttpRouterTls `json:"tls,omitempty"`
	Status      string         `json:"status"`
	Using       []string       `json:"using"`
	Name        string         `json:"name"`
	Provider    string         `json:"provider"`
}

type HttpRouterTls

type HttpRouterTls struct {
	Options string                `json:"options"`
	Domains []HttpRouterTlsDomain `json:"domains"`
}

type HttpRouterTlsDomain

type HttpRouterTlsDomain struct {
	Main string   `json:"main"`
	Sans []string `json:"sans"`
}

type ITraefikClient

type ITraefikClient interface {
	GetHttpRouters() (*[]HttpRouter, error)
}

type Traefik

type Traefik struct {
	Next          plugin.Handler
	Config        *TraefikConfig
	TraefikClient *TraefikClient

	Fall fall.F
	// contains filtered or unexported fields
}

func (*Traefik) Name

func (t *Traefik) Name() string

func (*Traefik) Ready

func (t *Traefik) Ready() bool

func (*Traefik) ServeDNS

func (t *Traefik) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error)

type TraefikClient

type TraefikClient struct {
	ITraefikClient
	// contains filtered or unexported fields
}

func NewTraefikClient

func NewTraefikClient(cfg *TraefikConfig) (*TraefikClient, error)

func (*TraefikClient) GetHttpRouters

func (c *TraefikClient) GetHttpRouters() (*[]HttpRouter, error)

type TraefikConfig

type TraefikConfig struct {
	// contains filtered or unexported fields
}

type TraefikConfigEntry

type TraefikConfigEntry struct {
	// contains filtered or unexported fields
}

type TraefikConfigEntryMap

type TraefikConfigEntryMap map[string]*TraefikConfigEntry

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL