132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
// 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 respective 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 (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/plugin"
|
|
"github.com/coredns/coredns/plugin/file"
|
|
"github.com/coredns/coredns/plugin/pkg/fall"
|
|
"github.com/coredns/coredns/plugin/pkg/upstream"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/go-ldap/ldap/v3"
|
|
)
|
|
|
|
type ldapRecord struct {
|
|
fqdn string
|
|
ip4 net.IP
|
|
ip6 net.IP
|
|
}
|
|
|
|
func (r *ldapRecord) A() (a *dns.A) {
|
|
return &dns.A{Hdr: dns.RR_Header{Name: r.fqdn, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}, A: r.ip4}
|
|
}
|
|
|
|
func (r *ldapRecord) AAAA() (a *dns.AAAA) {
|
|
return &dns.AAAA{Hdr: dns.RR_Header{Name: r.fqdn, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 86400}, AAAA: r.ip6}
|
|
}
|
|
|
|
|
|
// Ldap is an ldap plugin to serve zone entries from a ldap backend.
|
|
type Ldap struct {
|
|
Next plugin.Handler
|
|
Fall fall.F
|
|
Upstream *upstream.Upstream
|
|
Client ldap.Client
|
|
Zones file.Zones
|
|
|
|
// Exported for mocking in test
|
|
SearchRequest *ldap.SearchRequest
|
|
FqdnAttr string
|
|
Ip4Attr string
|
|
Ip6Attr string
|
|
|
|
ldapURL string
|
|
pagingLimit uint32
|
|
syncInterval time.Duration
|
|
username string
|
|
password string
|
|
sasl bool
|
|
zMu sync.RWMutex
|
|
ttl time.Duration
|
|
}
|
|
|
|
// New returns an initialized Ldap with defaults.
|
|
func New(zoneNames []string) *Ldap {
|
|
l := new(Ldap)
|
|
l.Zones.Names = zoneNames
|
|
l.pagingLimit = 0
|
|
l.syncInterval = 60 * time.Second
|
|
// SearchRequest defaults
|
|
l.SearchRequest = new(ldap.SearchRequest)
|
|
l.SearchRequest.DerefAliases = ldap.NeverDerefAliases // TODO: Reason
|
|
l.SearchRequest.Scope = ldap.ScopeWholeSubtree // search whole subtree
|
|
l.SearchRequest.SizeLimit = 500 // TODO: Reason
|
|
l.SearchRequest.TimeLimit = 500 // TODO: Reason
|
|
l.SearchRequest.TypesOnly = false // TODO: Reason
|
|
l.Zones.Z = make(map[string]*file.Zone, len(zoneNames))
|
|
|
|
for _, zn := range zoneNames {
|
|
l.Zones.Z[zn] = nil
|
|
}
|
|
|
|
return l
|
|
}
|
|
|
|
// InitClient initializes a Ldap client.
|
|
func (l *Ldap) InitClient() (err error) {
|
|
l.Client, err = ldap.DialURL(l.ldapURL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
err = l.Client.Bind(l.username, l.password)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := l.UpdateZones(); err != nil {
|
|
return err
|
|
}
|
|
|
|
defer l.Client.Close()
|
|
|
|
return nil
|
|
}
|
|
|
|
// SOA returns a syntetic SOA record for a zone.
|
|
func SOA(zone string) dns.RR {
|
|
ttl := uint32(300)
|
|
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: ttl, Class: dns.ClassINET}
|
|
|
|
Mbox := hostmaster + "."
|
|
Ns := "ns.dns."
|
|
|
|
if zone[0] != '.' {
|
|
Mbox += zone
|
|
Ns += zone
|
|
}
|
|
|
|
return &dns.SOA{Hdr: header,
|
|
Mbox: Mbox,
|
|
Ns: Ns,
|
|
Serial: 12345,
|
|
Refresh: 7200,
|
|
Retry: 1800,
|
|
Expire: 86400,
|
|
Minttl: ttl,
|
|
}
|
|
}
|
|
|
|
const hostmaster = "hostmaster"
|