// Package ldap is a CoreDNS plugin that resolves A, AAAA y PTR RR from a ldap backend. // // 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. package ldap import ( "context" "crypto/tls" "errors" "fmt" "strings" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/etcd/msg" "github.com/coredns/coredns/plugin/pkg/fall" "github.com/coredns/coredns/request" "github.com/miekg/dns" "gopkg.in/ldap.v3" ) // Ldap is an ldap plugin to serve zone entries from a ldap backend. type Ldap struct { Next plugin.Handler Fall fall.F Zones []string Client *ldap.Client clientConfig } // New returns an initialized Ldap with defaults. func New(zones []string) *Ldap { k := new(Ldap) k.Zones = zones return k } var ( errNoItems = errors.New("no items found") errNsNotExposed = errors.New("namespace is not exposed") errInvalidRequest = errors.New("invalid query name") ) // InitClient initializes a Ldap client. func (l *Ldap) InitClient() (err error) { l.Client, err = Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) if err != nil { log.Fatal(err) } defer l.Client.Close() // Reconnect with TLS err = l.Client.StartTLS(&tls.Config{InsecureSkipVerify: true}) if err != nil { log.Fatal(err) } } // 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 } services = msg.Group(services) return } // 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) } // 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) } // IsNameError implements the ServiceBackend interface. func (l *Ldap) IsNameError(err error) bool { return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest } // 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()) }