traefik_leaf_provider

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 14 Imported by: 0

README

traefik-leaf-provider

CI Go Reference

A Traefik provider plugin that aggregates HTTP router rules from downstream "leaf" Traefik instances, two-hop style: the main instance matches the leaf's Host rules and forwards matched traffic to the leaf's web entrypoint; the leaf does its own final routing.

One edge instance — the one holding your certificates, your forward-auth, your rate limits — serves the routes of an entire estate of downstream Traefik instances, and you never write a route on the edge by hand. One routing table, one API, every hostname in the estate.

Both hops are yours to secure. Each endpoint takes a private CA and a client keypair, and they apply to the forwarded traffic and to the plugin's own poll of the leaf's API — the connection Traefik itself has no say over, because the plugin makes it. So a leaf that refuses anything without a client certificate stays that way, and aggregating it does not mean punching a hole for an unauthenticated caller. See TLS.

Purpose-built after evaluating the existing aggregator plugins (im-kulikov/traefik-provider, MadddinTribleD/traefikaggregator, Beanow/traefik-plugin-rawdata) and finding each unmaintained or broken in ways that matter for a production edge: startup crashes on unreachable endpoints, multi-endpoint handling bugs, silent route breakage. This is the ~300-line subset that does only what is needed, with those failure modes as explicit design constraints.

Contents

Terms

This document uses these terms with one meaning each.

Term Meaning
main instance The Traefik instance at your edge. This plugin operates in the main instance.
leaf instance A Traefik instance downstream of the main instance.
endpoint One leaf instance, as configured in this plugin.
to aggregate To read the router rules from a leaf instance and make them available on the main instance.
poll cycle One pass in which the plugin reads each endpoint in turn.
base name A leaf router key with the @provider part removed. The key grafana@docker has the base name grafana.
emitted router A router that this plugin makes on the main instance.
two hops The path of a request. The client to the main instance is hop 1. The main instance to the leaf instance is hop 2.

How it operates

The plugin reads the API of each leaf instance at a regular interval. It reads the router rules, and it makes an equivalent router on the main instance. Each emitted router sends its traffic to the web entrypoint of that leaf instance. The leaf instance then does its own final routing.

flowchart TB
    client(["client"])
    main["<b>main instance</b><br/><i>this plugin operates here</i>"]
    leaf["<b>leaf instance</b>"]
    backend["grafana"]

    client -- "hop 1<br/>Host: grafana.example.com" --> main
    main -- "hop 2<br/>forward to webUrl" --> leaf
    leaf -- "the leaf instance does<br/>its own final routing" --> backend
    main -. "poll: GET apiUrl/api/rawdata<br/>every pollInterval" .-> leaf

The plugin does not copy the backend definitions from a leaf instance. The backends of a leaf instance have container-network addresses. The main instance cannot reach those addresses. The plugin makes one service for each endpoint instead. That service points at the web entrypoint of the leaf instance.

When to use this plugin

The usefulness of this plugin is in being able to have a central fronting traefik instance that manages auth middleware, certificates, etc, while still having downstream traefik instances on various platforms as suits your architecture. As well as helping solve architectural constraints, it centralises your routing information which helps it become a single source of truth you can pull into consumers like coredns-traefik — a CoreDNS plugin that generates CNAMEs from traefik http routers — for dynamic dns registration.

For example, you might have a k8s cluster traefik, a few vm's with a set of docker compose services, maybe you use the proxmox pve plugin to get vm-level traefik configs, and your design colocates the traefik that does that onto the proxmox instance. So you have a central traefik, a k8s leaf, 3 vm leafs, a proxmox leaf. Rather than wiring each of these up and trying to keep them all in line, you can keep your leafs simpler and your consuming services like CoreDNS simpler, which means your IaC is simpler and its easier to define and manage the overall mesh as a dynamic entity.

Two hops is the price slash design decision, we are not importing and bypassing the leaf, we are collating leaves as a routing operation. Every request traverses both instances, and the leaf sees the traffic.

If you have a really really intense setup, theres no real reason you couldnt heirarchically chain main -> branch -> leaf setups.

Do not use it if:

  • You do not control a leaf. The plugin copies router rules from every endpoint you configure. Anyone who can write a router rule on a leaf can create a route on your edge — that is what aggregation means. Configure only leaves you control.

Requirements

Item Value
Traefik v3. The E2E suite tests against v3.7. Earlier versions are not tested.
Leaf instance API The /api/rawdata route must be reachable from the main instance.
Network The main instance must reach the API URL and the web URL of each endpoint.
Go Only to build or test the plugin. Traefik interprets the source at runtime.

Restrict the leaf instance API to the main instance. The /api/rawdata route shows your full routing table. Use a firewall rule, a private network, or a middleware to control access to it.

Installation

There are two methods. Both are supported. Read the difference before you choose.

With this method Traefik reads the plugin from your disk. Traefik does not contact the plugin registry at any time, including at startup.

Choose this method for a production edge. A plugin on disk cannot fail to download.

  1. Get the source. Use one of these two commands.

    # From a release. Replace the version with the one you want.
    curl -fsSL -o plugin.tar.gz \
      https://github.com/NavistAu/traefik-leaf-provider/releases/download/v1.1.0/traefik-leaf-provider-v1.1.0.tar.gz
    
    # Or from Git, if you prefer to track a branch.
    git clone https://github.com/NavistAu/traefik-leaf-provider.git
    
  2. Put the source at this path, relative to the working directory of Traefik.

    plugins-local/src/github.com/NavistAu/traefik-leaf-provider/
    

    The vendor/ directory must be present. It is part of the repository and part of the release tarball.

  3. Declare the plugin in your static configuration.

    experimental:
      localPlugins:
        leafprovider:
          moduleName: github.com/NavistAu/traefik-leaf-provider
    

For Docker, mount the source into the container.

services:
  traefik:
    image: traefik:v3.7
    volumes:
      - ./traefik-leaf-provider:/plugins-local/src/github.com/NavistAu/traefik-leaf-provider:ro
Method 2: plugin catalog

With this method Traefik downloads the plugin from the plugin catalog.

experimental:
  plugins:
    leafprovider:
      moduleName: github.com/NavistAu/traefik-leaf-provider
      version: v1.1.0

Know the cost of this method. Traefik contacts plugins.traefik.io at startup. If that download fails, Traefik does not start. Your edge then serves no routes at all. Method 1 has no such dependency.

Configuration

Declare the provider in your static configuration.

providers:
  plugin:
    leafprovider:
      pollInterval: 5s
      requestTimeout: 3s
      endpoints:
        - name: alpha
          apiUrl: http://192.0.2.10:8080
          webUrl: http://192.0.2.10:80
          middlewares:
            - forward-auth@file
          routeMiddlewares:
            grafana: []
            status:
              - forward-auth@file

The key leafprovider must match the key you used in the experimental plugin configuration .

Top-level options
Option Type Default Description
pollInterval duration 5s The time between poll cycles. Must be more than zero.
requestTimeout duration 3s The timeout for one API call to one leaf instance. Must be more than zero.
endpoints list The leaf instances. You must give at least one.

A duration is a Go duration string, such as 5s, 500ms, or 1m.

Set pollInterval to a value that suits your rate of change. A short interval finds new routes sooner. It also makes more requests to each leaf instance.

Keep requestTimeout well below pollInterval. The plugin reads the endpoints one after the other, not at the same time. If every endpoint times out, one poll cycle takes requestTimeout multiplied by the number of endpoints.

Endpoint options
Option Type Required Description
name string Yes Identifies the endpoint. Must be unique. The plugin puts this name into each emitted router name.
apiUrl string Yes The base URL of the leaf instance API. The plugin adds /api/rawdata to it.
webUrl string Yes The web entrypoint of the leaf instance. The plugin sends matched traffic here.
middlewares list of string No Middlewares to attach to every router emitted for this endpoint.
routeMiddlewares map of string to list of string No Middlewares for named routes. Replaces middlewares for those routes. See Middlewares.
tls block No TLS for both hops of this endpoint: a private CA, a client keypair, or both. See TLS.
serversTransport string No Names an existing Traefik serversTransport for hop 2. Overrides the one generated from tls.

Give each middleware its full name, with the provider part. Write forward-auth@file, not forward-auth. The middleware must exist on the main instance. The plugin does not copy middlewares from a leaf instance.

Configuration errors

Traefik does not start if the configuration is invalid. The plugin rejects these cases with a clear message.

Message Cause
at least one endpoint is required The endpoints list is absent or empty.
invalid pollInterval "…" The value does not parse, or it is not more than zero.
invalid requestTimeout "…" The value does not parse, or it is not more than zero.
endpoint N: name, apiUrl and webUrl are all required Endpoint number N has an empty required field. N starts at 0.
duplicate endpoint name "…" Two endpoints have the same name.

Names of the emitted routers and services

The plugin builds each emitted router name from two parts.

<base name of the leaf router>-<endpoint name>

The service name for an endpoint is:

<endpoint name>-leaf

For example, the leaf router grafana@docker on the endpoint alpha becomes the router grafana-alpha. That router uses the service alpha-leaf.

Traefik adds its own provider part to these names. In the Traefik API and dashboard you see grafana-alpha@plugin-leafprovider.

The plugin copies the rule and the priority of each leaf router. It sets the service and the middlewares itself.

Keep your endpoint names short and distinct. The endpoint name is part of every router name that comes from that endpoint.

Two endpoints that serve the same host

The plugin gives each emitted router a unique name, because the endpoint name is part of it. It does not make the rules unique. Two leaf instances that both serve Host(\app.example.com`)` give two routers with the same rule and the same priority. Traefik then picks one of them, and which one it picks is not defined.

This matters most when two endpoints point at the same leaf instance, because then every rule collides.

Do one of these:

  • Give each leaf instance its own host names. This is the usual answer.
  • Set a different priority on the leaf router that must win. The plugin copies the priority from the leaf instance.
Routers that the plugin ignores

The plugin does not emit a router in these cases.

  • The leaf router key ends with @internal. These are the internal routers of the leaf instance, such as its API and its dashboard.
  • The leaf router has an empty rule.

If an endpoint gives no usable routers, the plugin makes no service for it either.

Middlewares

There are two levels. The per-route level replaces the endpoint level. It does not add to it.

Endpoint level

middlewares attaches to every router emitted for that endpoint. Use this for a concern that applies to the whole leaf instance, such as forward authentication.

- name: alpha
  apiUrl: http://192.0.2.10:8080
  webUrl: http://192.0.2.10:80
  middlewares:
    - forward-auth@file

Every route from alpha now goes through forward-auth@file.

Per-route level

routeMiddlewares sets the middlewares for named routes. The keys are base names of leaf routers. The rawdata key grafana@docker has the base name grafana.

- name: alpha
  apiUrl: http://192.0.2.10:8080
  webUrl: http://192.0.2.10:80
  middlewares:
    - forward-auth@file
  routeMiddlewares:
    grafana: []
    status:
      - forward-auth@file
      - ratelimit@file

This configuration gives three different results.

Leaf router Middlewares attached Reason
grafana@docker none The key is present with an empty list.
status@docker forward-auth@file, ratelimit@file The key is present with a list. That list replaces the endpoint list.
prometheus@docker forward-auth@file No key is present. The endpoint list applies.

The plugin resolves the middlewares for each router this way.

flowchart TD
    A["a leaf router,<br/>e.g. grafana@docker"] --> B{"is its base name a key<br/>in routeMiddlewares?"}
    B -- "no" --> C["attach the endpoint<br/>middlewares list"]
    B -- "yes" --> D{"is that key's value<br/>an empty list?"}
    D -- "yes" --> E["attach nothing"]
    D -- "no" --> F["attach that list, and<br/>ignore the endpoint list"]

Read these rules again, because the behaviour is deliberate.

  1. A key that is present replaces the endpoint list for that route. The plugin does not merge the two lists.
  2. A key with an explicitly empty list ([]) attaches no middlewares. This is how you exempt one route from an otherwise gated endpoint.
  3. A route with no key falls back to the endpoint list.

Check the log after you add or rename a key. A key with a typographical error matches no route. The route you meant to gate then serves with the endpoint middlewares, or with none. The plugin reports unmatched keys. See the next section.

TLS

A request crosses two connections, and they are configured in different places. Know which one you are changing.

flowchart LR
    client(["client"]) -- "hop 1<br/>edge TLS termination<br/><i>entryPoints config</i>" --> main["main instance"]
    main -- "hop 2<br/>main to leaf<br/><i>endpoint tls / serversTransport</i>" --> leaf["leaf instance"]
    main -. "the poll<br/><i>endpoint tls only</i>" .-> leaf
Connection Who makes it You configure it with
Client to main Traefik entryPoints in the static configuration
Main to leaf (traffic) Traefik The endpoint tls block, or serversTransport
Main to leaf API (poll) This plugin The endpoint tls block only

The poll is the important row. The plugin makes that connection itself, with its own HTTP client. Traefik has no part in it. Without a tls block, the plugin uses the trust store of its container and sends no client certificate.

The endpoint tls block

One block configures both the poll and the traffic hop. The plugin builds its own client for the poll. It also emits an equivalent Traefik serversTransport and points the endpoint's service at it.

endpoints:
  - name: alpha
    apiUrl: https://leaf-alpha.example.com:8080
    webUrl: https://leaf-alpha.example.com:443
    tls:
      rootCAs:
        - /certs/internal-ca.pem
      certificates:
        - certFile: /certs/main-instance.crt
          keyFile: /certs/main-instance.key
      serverName: leaf-alpha.example.com
      insecureSkipVerify: false
Option Type Default Description
rootCAs list of string Paths to PEM bundles. The plugin adds these to the system trust store. It does not replace it.
certificates list of block Client keypairs to present to the leaf instance. Each needs certFile and keyFile.
serverName string Overrides SNI and the verified host name. Use this when you address the leaf instance by IP.
insecureSkipVerify boolean false Turns off verification of the leaf certificate.

All paths are read inside the main instance. Mount the files into that container.

Do not set insecureSkipVerify in production. It removes the proof that you are connected to the leaf instance and not to something else. The plugin logs a warning at startup when you set it.

Mutual TLS

Give certificates to present a client keypair to the leaf instance. This works on both hops. The E2E suite tests the poll against a leaf API that rejects a caller with no client certificate.

The plugin reads the keypair once, and then keeps it. To use a new keypair, restart the main instance.

Using your own serversTransport

Set serversTransport to hand hop 2 to a transport that you define. The plugin then makes no transport of its own for that endpoint. Use this for options that the tls block does not model, such as SPIFFE, cipher suites, or forwarding timeouts.

endpoints:
  - name: alpha
    apiUrl: https://leaf-alpha.example.com:8080
    webUrl: https://leaf-alpha.example.com:443
    serversTransport: spiffe-mesh@file
    tls:
      rootCAs:
        - /certs/internal-ca.pem

In this example spiffe-mesh@file governs hop 2. The tls block still governs the poll. This is the only way to make the two hops different.

serversTransport alone, with no tls block, leaves the poll on system trust. That is correct when the leaf API uses a publicly trusted certificate but the traffic hop needs a private mesh identity.

Names of the generated transport

The plugin names the transport it generates <endpoint name>-transport. Traefik shows it as <endpoint name>-transport@plugin-leafprovider. An endpoint with no tls block and no serversTransport keeps Traefik's global default transport, which is the behaviour of v1.

TLS failures are isolated

A TLS problem behaves like any other endpoint failure. It never stops Traefik.

Event What the plugin does
A certificate file is absent or unreadable The endpoint fails, logs, and serves the last good configuration. It retries next poll.
A PEM file holds no certificate Same. The log says no PEM certificates found.
The leaf certificate does not verify Same. The log holds the verification error.
certFile is set but keyFile is not Traefik does not start. A half-specified keypair can never become valid.

The plugin does not read the certificate files during initialisation. A secret that is still being mounted during a restart therefore degrades one endpoint for one poll, instead of stopping every route in the estate.

TLS at the edge

The plugin does not set a tls section on the routers it emits. Those routers do not terminate TLS by themselves. Use an entrypoint default to serve aggregated routes over HTTPS.

entryPoints:
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

This turns on TLS for every router on that entrypoint, which includes every aggregated router.

The plugin attaches its routers to every entrypoint. It does not set entryPoints on them. A plain HTTP entrypoint therefore also serves every aggregated route. Give that entrypoint a redirection to your HTTPS entrypoint, or remove it.

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

Behaviour when a leaf instance fails

The plugin keeps each endpoint separate. A failure at one endpoint does not affect another.

Event What the plugin does
Traefik starts and a leaf instance is down Traefik starts normally. The plugin makes no network call during initialisation.
A poll of one endpoint fails The plugin logs the error. It serves the last good configuration for that endpoint. Other endpoints are unaffected.
A poll fails and there is no last good configuration The plugin emits no routers for that endpoint. Other endpoints are unaffected.
A poll succeeds again The plugin replaces the last good configuration with the new one.
A route disappears from a leaf instance The plugin removes the emitted router at the next successful poll of that endpoint.

The rule behind that table: stale beats blank. A stale route usually still works — the backend is still there, and the leaf still knows how to reach it. An empty configuration hard-404s every aggregated host on the estate, which is a far worse outcome than serving a route that has moved on.

Log messages

Every message from this plugin starts with leaf-provider, followed by the provider name you configured.

An endpoint failed
leaf-provider leafprovider: endpoint "alpha": fetch: dial tcp 192.0.2.10:8080: connect: connection refused (serving last known good)

The plugin could not read this endpoint. It logs this on every failed poll cycle. The text after endpoint "alpha": states the cause.

Text Cause
fetch: … The main instance could not reach the leaf instance API. Check the network and apiUrl.
fetch: HTTP 404 The URL is wrong. Give the base URL, not the /api/rawdata path.
fetch: HTTP 401 or HTTP 403 The leaf instance API needs authentication. This plugin cannot supply it.
decode: … The response is not the expected rawdata format. Check that apiUrl points at a Traefik API.
A routeMiddlewares key matched no route
leaf-provider leafprovider: endpoint "alpha": routeMiddlewares keys matching no leaf router: [staus]

One or more keys in routeMiddlewares match no router on that leaf instance. The usual cause is a typographical error in the key, as in the example above.

The plugin logs this when the set of unmatched keys changes. It does not log it on every poll cycle. Look for this message after you add or rename a key.

Treat this message as a security problem, not as a warning. The route you meant to gate is not gated the way you intended.

Troubleshooting

Traefik does not start

Read the Traefik log. The plugin reports every configuration error with a clear message. See Configuration errors.

If the message is about the plugin itself, and not about the configuration, check that the vendor/ directory is present at the install path.

No routes appear

Do these checks in order.

  1. Confirm that the provider loaded. Look for plugin-leafprovider in the Traefik dashboard, or in /api/rawdata on the main instance.

  2. Look for a leaf-provider line in the log. A failed endpoint always logs.

  3. Confirm that the main instance can reach the leaf instance API.

    curl -s http://192.0.2.10:8080/api/rawdata | head -c 200
    
  4. Confirm that the leaf instance has routers with rules.

    curl -s http://192.0.2.10:8080/api/rawdata | jq '.routers | keys'
    

    The plugin ignores routers that end with @internal. This is intended.

  5. Wait for one pollInterval. The first poll happens immediately. A route that appears downstream needs the next cycle.

A route appears but returns an error

The router exists on the main instance, so the aggregation works. The problem is at hop 2.

Test the leaf instance directly. Use the Host header of the route.

curl -i -H 'Host: grafana.example.com' http://192.0.2.10:80/

If this fails, the problem is in the leaf instance or in its backend, not in this plugin. If it succeeds, check webUrl and check the middlewares on the main instance.

A middleware does not apply
  1. Read /api/rawdata on the main instance, and find the emitted router.

    curl -s http://127.0.0.1:8080/api/rawdata \
      | jq '.routers["grafana-alpha@plugin-leafprovider"].middlewares'
    
  2. If the list is wrong, look for a routeMiddlewares key with that base name. A key that is present replaces the endpoint list.

  3. If the list is empty and you did not intend that, look for the unmatched-key message in the log.

  4. If the list is correct but the middleware does nothing, confirm that the middleware exists on the main instance. It must have that exact name, including the provider part.

Routes disappear and return

The endpoint fails some polls and succeeds at others. Look for serving last known good lines in the log. Raise requestTimeout, or find why the leaf instance API is slow.

Design constraints (why the code looks like it does)

Each of these broke one of the prior candidates. They are not stylistic preferences, and "cleaning them up" is how this plugin becomes the fourth broken aggregator. Read provider.go's package comment before touching any of them.

  • No network I/O in Init() — provider init failure is fatal to Traefik; a blocking probe there turns "one leaf down during a restart" into "every route in the estate down".
  • Sequential polling, per-endpoint error isolation, per-endpoint last-known-good — one dead leaf never blocks or blanks another; transient API failures serve stale rather than empty. Concurrency is precisely where the prior candidates' endpoint bugs lived.
  • Backends are never copied from leaves — leaf backend URLs are container-network addresses, unreachable from the aggregator. Interpreting a proxy's internals in order to route around it is the design error this plugin exists to avoid. One service per leaf, pointing at its web entrypoint.
  • Yaegi-safe idioms — this runs under Traefik's Go interpreter, not the compiler. Index-based loops, because loop-variable capture diverges and shipped exactly that bug in a prior plugin. Plain channel sends into the provider channel, because a select-case send of an interpreted value panics in reflect.Select — found empirically, in e2e, on 2026-07-23. Compiled unit tests are necessary but nowhere near sufficient.

Tests

go test ./...          # compiled unit tests
./test/e2e/run.sh      # real Traefik, real Yaegi

The compiled suite covers the transform and merge logic, and it is fast. It is also incomplete by nature: Traefik does not use the Go compiler to run this plugin, so a green go test proves remarkably little about whether the thing actually works.

test/e2e/run.sh is the gate that counts. It starts a real Traefik instance that loads this repository as a local plugin, against one live leaf instance, one deliberately dead endpoint, and an nginx gate that fronts a leaf API with ssl_verify_client on. It asserts these things.

  • Startup survives the dead endpoint.
  • Aggregation works, and the emitted names are correct.
  • A request goes through both hops.
  • The emitted routers have the correct provider namespace.
  • The plugin logs the dead endpoint and isolates it.
  • The four routeMiddlewares behaviours hold.
  • An endpoint aggregates through the mutual TLS gate.
  • The same gate rejects an endpoint that sends no client certificate.
  • The generated serversTransport resolves.
  • The plain routes still work next to all of it.

The mutual TLS assertions are the reason this suite exists. The plugin builds its TLS client under the interpreter, and only a real handshake shows that it works.

You need Docker with the Compose plugin. The suite makes its own certificates on the first run, in test/e2e/certs/. That directory is not in Git.

Contributing

Read CONTRIBUTING.md. It covers the branch model, the two test suites, and the Yaegi constraints that reviewers check.

This project follows the Contributor Covenant.

Security

Report a vulnerability privately. Follow SECURITY.md. Do not open a public issue for a vulnerability.

License

MIT. See LICENSE.

Documentation

Overview

Package traefik_leaf_provider aggregates HTTP router rules from downstream "leaf" traefik instances into this (main) traefik, using the two-hop model: main matches the leaf's Host rules and forwards the whole request to the leaf's web entrypoint; the leaf does its own final routing. Backend/service definitions are deliberately NOT copied from leaves — their backend URLs are container-network addresses that are unreachable from here, and interpreting a proxy's internals to route around it is the design error this plugin exists to avoid.

Hard-won constraints (each broke a prior candidate plugin — do not "clean up" without understanding them):

  • Init() must do NO network I/O: traefik treats provider init failure as fatal, so a blocking probe here turns "one leaf down during a restart" into "every route in the estate down".
  • Endpoints are polled sequentially with per-endpoint error isolation and per-endpoint last-known-good: one dead leaf must never block or blank the others.
  • Loops over endpoints use explicit indexing: this code runs under Yaegi (traefik's interpreter), whose loop-variable capture semantics have diverged from compiled Go and shipped exactly that bug in a prior plugin.
  • TLS material is NEVER read during New()/Init(), only lazily at poll time: a cert path that is momentarily unreadable (a secret still mounting during a restart) must degrade one endpoint, not abort provider init and take the whole estate down with it. Only shape errors that can never resolve — a keypair missing one half — are rejected up front.

crypto/tls, crypto/x509 and os.ReadFile were all confirmed to resolve under Yaegi against traefik v3.7 (spiked 2026-07-30); test/e2e drives the full mTLS path so a future interpreter change cannot regress it silently.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Certificate

type Certificate struct {
	CertFile string `json:"certFile" yaml:"certFile"`
	KeyFile  string `json:"keyFile" yaml:"keyFile"`
}

Certificate is one client-certificate keypair, given as file paths. Field names mirror traefik's own serversTransport certificates so an operator writes the same shape in both places.

type ClientTLS

type ClientTLS struct {
	// RootCAs are PEM bundle paths, ADDED to the system pool rather than
	// replacing it. Empty means system trust only.
	RootCAs []string `json:"rootCAs" yaml:"rootCAs"`
	// Certificates are client keypairs presented to the leaf (mTLS).
	Certificates []Certificate `json:"certificates" yaml:"certificates"`
	// ServerName overrides SNI and the verified hostname. Use when the
	// endpoint is addressed by IP but the certificate names a host.
	ServerName string `json:"serverName" yaml:"serverName"`
	// InsecureSkipVerify disables verification of the leaf's certificate.
	// This removes the guarantee that you are talking to the leaf at all.
	InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
}

ClientTLS configures TLS for BOTH hops of one endpoint:

  • hop 1, this plugin's own poll of the leaf's API. Traefik has no say in this connection — it is a plain net/http client inside the plugin — so without this block there is no way to reach a leaf API behind a private CA or a client-certificate requirement.
  • hop 2, traefik's forward to the leaf's web entrypoint. The plugin emits an equivalent serversTransport and points the endpoint's service at it, so one block gates both hops the same way.

Set Endpoint.ServersTransport instead to hand hop 2 to a transport you define yourself; it takes precedence over the generated one.

type Config

type Config struct {
	// PollInterval between refresh cycles. Default "5s".
	PollInterval string `json:"pollInterval" yaml:"pollInterval"`
	// RequestTimeout for each leaf API call. Default "3s".
	RequestTimeout string     `json:"requestTimeout" yaml:"requestTimeout"`
	Endpoints      []Endpoint `json:"endpoints" yaml:"endpoints"`
}

Config is the plugin configuration.

func CreateConfig

func CreateConfig() *Config

CreateConfig is required by traefik's plugin loader.

type Endpoint

type Endpoint struct {
	// Name namespaces the emitted routers (router "grafana@docker" on
	// leaf "alpha" becomes "grafana-alpha"). Required, unique.
	Name string `json:"name" yaml:"name"`
	// APIURL is the base URL of the leaf's traefik API, e.g.
	// "http://192.0.2.10:8080". "/api/rawdata" is appended.
	APIURL string `json:"apiUrl" yaml:"apiUrl"`
	// WebURL is where matched traffic is forwarded — the leaf's web
	// entrypoint, e.g. "http://192.0.2.10:80".
	WebURL string `json:"webUrl" yaml:"webUrl"`
	// Middlewares are attached to every router emitted for this
	// endpoint, referenced with their full provider suffix, e.g.
	// "forward-auth@file". Optional.
	Middlewares []string `json:"middlewares" yaml:"middlewares"`
	// RouteMiddlewares overrides Middlewares per router, keyed by the
	// leaf router's base name (rawdata key with its "@provider" suffix
	// stripped, e.g. "grafana-leaf" for rawdata key
	// "grafana-leaf@docker" — the same base name used to build the
	// emitted router name). A present key REPLACES Middlewares for that
	// router, including an explicitly-empty list (no middlewares). A
	// router with no key here falls back to Middlewares. Optional.
	RouteMiddlewares map[string][]string `json:"routeMiddlewares" yaml:"routeMiddlewares"`
	// TLS configures both hops for this endpoint. Optional; without it
	// hop 1 uses system trust and hop 2 uses traefik's global default
	// serversTransport, which is the v1 behaviour.
	TLS *ClientTLS `json:"tls" yaml:"tls"`
	// ServersTransport names an existing traefik serversTransport for
	// hop 2, e.g. "leaf-mtls@file". Takes precedence over the transport
	// generated from TLS. Optional.
	ServersTransport string `json:"serversTransport" yaml:"serversTransport"`
}

Endpoint is one leaf traefik instance.

type Provider

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

Provider implements traefik's plugin provider interface.

func New

func New(ctx context.Context, config *Config, name string) (*Provider, error)

New is called by traefik with the parsed config. No I/O here.

func (*Provider) Init

func (p *Provider) Init() error

Init must stay free of network I/O — see package comment.

func (*Provider) Provide

func (p *Provider) Provide(cfgChan chan<- json.Marshaler) error

Provide starts the poll loop. Returning promptly is required; polling happens in the goroutine.

func (*Provider) Stop

func (p *Provider) Stop() error

Stop terminates the poll loop.

Jump to

Keyboard shortcuts

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