2020-06-01 07:59:41 +00:00
|
|
|
// Package ldap is a CoreDNS plugin that resolves A, AAAA y PTR RR from a ldap backend.
|
2018-04-01 11:56:41 +00:00
|
|
|
//
|
2020-06-01 07:59:41 +00:00
|
|
|
// It serves as a backend connector for autoritative zone data.
|
|
|
|
// Ldap is often used for bare metal inventories. This use is the main use case
|
|
|
|
// for this plugin. Other use cases might eventually be supported.
|
|
|
|
// fqdn and ip4 / ip6 information is mapped from it's repsective ldap schema and
|
|
|
|
// served as DNS records over coredns. Mapping is configurable. To reduce load
|
|
|
|
// on the backend, a configurable cache is bundled.
|
2020-06-01 07:48:07 +00:00
|
|
|
package ldap
|
2017-07-29 08:17:24 +00:00
|
|
|
|
|
|
|
import (
|
2019-09-24 06:16:55 +00:00
|
|
|
"context"
|
2020-06-04 17:41:04 +00:00
|
|
|
"crypto/tls"
|
2020-06-03 16:25:04 +00:00
|
|
|
"errors"
|
2017-07-29 08:17:24 +00:00
|
|
|
"fmt"
|
2020-06-04 17:41:04 +00:00
|
|
|
"strings"
|
2017-07-29 08:17:24 +00:00
|
|
|
|
2017-09-15 20:29:47 +00:00
|
|
|
"github.com/coredns/coredns/plugin"
|
2020-06-04 17:41:04 +00:00
|
|
|
"github.com/coredns/coredns/plugin/etcd/msg"
|
2020-06-03 16:25:04 +00:00
|
|
|
"github.com/coredns/coredns/plugin/pkg/fall"
|
2020-06-04 17:41:04 +00:00
|
|
|
"github.com/coredns/coredns/request"
|
2018-02-27 11:39:01 +00:00
|
|
|
|
2017-07-29 08:17:24 +00:00
|
|
|
"github.com/miekg/dns"
|
2020-06-03 17:47:21 +00:00
|
|
|
"gopkg.in/ldap.v3"
|
2017-07-29 08:17:24 +00:00
|
|
|
)
|
|
|
|
|
2020-06-01 07:59:41 +00:00
|
|
|
// Ldap is an ldap plugin to serve zone entries from a ldap backend.
|
2020-06-01 07:48:07 +00:00
|
|
|
type Ldap struct {
|
2020-06-04 17:41:04 +00:00
|
|
|
Next plugin.Handler
|
|
|
|
Fall fall.F
|
|
|
|
Zones []string
|
|
|
|
Client *ldap.Client
|
2020-06-07 19:29:18 +00:00
|
|
|
clientConfig map[string]
|
2020-06-03 16:25:04 +00:00
|
|
|
}
|
2018-04-19 06:25:30 +00:00
|
|
|
|
2020-06-03 17:47:21 +00:00
|
|
|
// New returns an initialized Ldap with defaults.
|
|
|
|
func New(zones []string) *Ldap {
|
|
|
|
k := new(Ldap)
|
|
|
|
k.Zones = zones
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
var (
|
|
|
|
errNoItems = errors.New("no items found")
|
|
|
|
errNsNotExposed = errors.New("namespace is not exposed")
|
|
|
|
errInvalidRequest = errors.New("invalid query name")
|
|
|
|
)
|
2018-02-25 08:52:52 +00:00
|
|
|
|
2020-06-04 17:41:04 +00:00
|
|
|
// InitClient initializes a Ldap client.
|
2020-06-03 17:47:21 +00:00
|
|
|
func (l *Ldap) InitClient() (err error) {
|
2020-06-07 19:29:18 +00:00
|
|
|
l.Client, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
|
2020-06-03 17:47:21 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2020-06-04 17:41:04 +00:00
|
|
|
defer l.Client.Close()
|
2020-06-03 17:47:21 +00:00
|
|
|
|
|
|
|
// Reconnect with TLS
|
2020-06-04 17:41:04 +00:00
|
|
|
err = l.Client.StartTLS(&tls.Config{InsecureSkipVerify: true})
|
2020-06-03 17:47:21 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
// Services implements the ServiceBackend interface.
|
|
|
|
func (l *Ldap) Services(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (services []msg.Service, err error) {
|
|
|
|
services, err = l.Records(ctx, state, exact)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2018-04-01 11:56:41 +00:00
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
services = msg.Group(services)
|
|
|
|
return
|
2017-07-29 08:17:24 +00:00
|
|
|
}
|
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
// Reverse implements the ServiceBackend interface.
|
|
|
|
func (l *Ldap) Reverse(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (services []msg.Service, err error) {
|
|
|
|
return l.Services(ctx, state, exact, opt)
|
2017-07-29 08:17:24 +00:00
|
|
|
}
|
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
// Lookup implements the ServiceBackend interface.
|
|
|
|
func (l *Ldap) Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
|
|
|
return l.Upstream.Lookup(ctx, state, name, typ)
|
2017-07-29 08:17:24 +00:00
|
|
|
}
|
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
// IsNameError implements the ServiceBackend interface.
|
|
|
|
func (l *Ldap) IsNameError(err error) bool {
|
|
|
|
return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest
|
2017-07-29 08:17:24 +00:00
|
|
|
}
|
2018-01-26 18:43:28 +00:00
|
|
|
|
2020-06-03 16:25:04 +00:00
|
|
|
// Records looks up records in ldap. If exact is true, it will lookup just this
|
|
|
|
// name. This is used when find matches when completing SRV lookups for instance.
|
|
|
|
func (l *Ldap) Records(ctx context.Context, state request.Request, exact bool) ([]msg.Service, error) {
|
|
|
|
name := state.Name()
|
|
|
|
|
|
|
|
path, star := msg.PathWithWildcard(name, l.PathPrefix)
|
|
|
|
r, err := l.get(ctx, path, !exact)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
segments := strings.Split(msg.Path(name, l.PathPrefix), "/")
|
|
|
|
return l.loopNodes(r.Kvs, segments, star, state.QType())
|
2020-06-03 17:47:21 +00:00
|
|
|
}
|