corednsyaegi

package module
v0.0.0-...-237f8b0 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2024 License: Apache-2.0 Imports: 13 Imported by: 0

README

coredns-yaegi

A CoreDNS plugin that allows loading other plugins on the fly using Yaegi engine.

Why do I need this?

So, maybe you are wondering... we already have regular CoreDNS plugins, why should I need this?

Opposite to the regular CoreDNS plugins, using this plugin, you don't need to recompile coreDNS and configure it to add them, Yaegi will be able to load them on the fly and execute as a restricted sandboxed Go runtime, however, these plugins are very limited compared to the regular plugins, Depending on the use case one or the other would be the way to go.

Pros
  • Easy to load.
  • Fast to develop and setup.
  • Easy to maintain.
  • Portable.
Cons
  • Limited to the usage standard library and some CoreDNS packages.
  • No external libraries allowed.
  • Need to be on a single file.
  • A bit less performant.
  • Limited by Yaegi features and bugs

So, checking the pros and cons, you may get the idea of these, when you need a simple plugin like blocking/allow DNS, rewriting/mutating the DNS request... these plugins are very easy to set and useful. On the opposite, if you want to connect with external services like a Redis or Kubernetes, you should stick to regular plugins.

Features and restrictions

  • Made in Go.
  • Heavily dependant on Yaegi.
  • Almost similar plugin syntax to the regular coreDNS plugins.
  • No external dependencies allowed (except some CoreDNS and helper packages).
  • Ability to measure using CoreDNS plugins Prometheus.
  • Plugin must be on a single file.
  • Configurable from a local file or to download from an HTTP endpoint (e.g public repo or public/private gist).
  • Can load multiple plugins from different files at the same time in a specific order.
  • Can pass each plugin any kind of string configuration.

Use cases

  • DNS allow/block logic.
  • Complex logic based DNS rewriting.
  • Audit.

Writing plugins

To write a plugin you only need to implement a method: func NewPlugin(next corednsplugin.Handler, rawOptions string) corednsplugin.Handler.

This method should return the plugin itself. Example of a NOOP plugin:

package noop

import (
    "context"

    corednsplugin "github.com/coredns/coredns/plugin"
    "github.com/miekg/dns"
)

type plugin struct {
    next corednsplugin.Handler
}

func NewPlugin(next corednsplugin.Handler, rawOptions string) corednsplugin.Handler {
   return plugin{next: next}
}

func (p plugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
    return corednsplugin.NextOrFailure(p.Name(), p.next, ctx, w, r)
}

func (p plugin) Name() string { return "noop" }
Options

You have two ways of passing options to plugins.

Raw options

When declaring the plugin on the CoreConfig file, optionally, you can pass as an argument a string, this string can be anything, so when the factory is called from the yaegi plugin, the plugin will receive that string, then each plugin can do whatever it wants with the rawOptions string, usage ideas/examples:

  • Simple string: opt1.
  • A full JSON config: {"opt1": "val1", "opt2": true}.
  • A simple key value: opt1=val1,opt2=val2.
Env vars

Plugins will have access to the OS env vars, so you can load from there options, secrets...

Combination of both

As an idea, you can combine raw options and env vars, for example a full config file and some keys will have ${SOMETHING}, so the plugin can load ${SOMETHING} from the env

Allowed packages?

Besides the ability to use all the Go standard library, you can use these external packages:

Do you miss any package? Depending on the general usefulness and safety we may add it so is available.

Configuration

The configuration is very simple, a yaegi plugin can load a single plugin per block (if you need more, you can make your plugin have a chaining logic), and it can be loaded from a local file or a public HTTP URL. also the plugins can receive an string as an argument that will be passed to the plugins on load. Lets see some examples:

Load a single plugin from a local file without options on a single line:

. {
    yaegi { /tmp/my-plugin.go }
    forward . 1.1.1.1
}

Load from a URL with options in a single line:

. {
    yaegi { https://raw.githubusercontent.com/slok/coredns-yaegi/main/_examples/allowlist/plugin.go "k1=v1,k2=v2" }
    forward . 1.1.1.1
}

Multiple plugins from different sources, some with different options:

   yaegi {
        /plugins/plugin1.go "{\"opt1\": true, \"opt2\": \"something\"}"
        http://plugins.example.com/plugin2.go
        /plugins/plugin3.go "opt1=val1,opt2=val2"
    }

Multiple blocks and different plugins and different options:

slok.dev {
    log
    yaegi {/plugins/slok.go }
    forward . 1.1.1.1
}

google.com {
    log
    errors
    forward . 1.1.1.1
}

twitter.com {
    log
    yaegi { ./twitter.go "something" }
    forward . 8.8.8.8
}

. {
    forward . 8.8.8.8
    log
    yaegi { https://example.com/generic.go "{\"opt1\": \"val1\"}" }
    errors
    cache
}

Ready to use image

You can build you own coreDNS image and set this plugin using CoreDNS docs, however we provide a CoreDNS image that has the yaegi plugin ready to be used:

docker pull ghcr.io/slok/coredns-yaegi

Example

Let's load our example allowlist plugin directly from an URL file.

$ cat ./coredns.config
. {
    yaegi { https://raw.githubusercontent.com/slok/coredns-yaegi/main/_examples/allowlist/plugin.go }
    forward . 1.1.1.1
}

$ docker run --rm -it --network host \
    -v ${PWD}/coredns.config:/tmp/coredns.config \
    ghcr.io/slok/coredns-yaegi:2620714c574e80582ff0072086acbc6c53072d08 \
    --conf /tmp/coredns.config  -dns.port=1053

$ dig @localhost -p 1053 a github.com +short
$ dig @localhost -p 1053 a google.com +short
$ dig @localhost -p 1053 a xabi.dev +short
172.67.193.60
104.21.20.171

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CoreDNSPlugin

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

func NewCoreDNSPlugin

func NewCoreDNSPlugin(config CoreDNSPluginConfig) (*CoreDNSPlugin, error)

func (CoreDNSPlugin) Name

func (c CoreDNSPlugin) Name() string

func (CoreDNSPlugin) Ready

func (c CoreDNSPlugin) Ready() bool

func (CoreDNSPlugin) ServeDNS

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

type CoreDNSPluginConfig

type CoreDNSPluginConfig struct {
	NextPlugin corednsplugin.Handler
	PluginsSrc string
	RawOptions string
}

type PluginSrcWithOpts

type PluginSrcWithOpts struct {
	Src        string
	RawOptions string
}

func ConfigParse

func ConfigParse(c *caddy.Controller) ([]PluginSrcWithOpts, error)

Directories

Path Synopsis
_examples
internal

Jump to

Keyboard shortcuts

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