mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-24 20:47:12 +10:00
Refactor test DNS server into separate package
Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
@@ -39,7 +39,10 @@ filegroup(
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//test/acme/dns/server:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"rfc2136.go",
|
||||
"server.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/test/acme/dns/server",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/logs:go_default_library",
|
||||
"//vendor/github.com/go-logr/logr:go_default_library",
|
||||
"//vendor/github.com/miekg/dns:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package server implements an extremely basic DNS server that only responds
|
||||
// to a very limited subset of DNS requests.
|
||||
// It is suitable for use during testing RFC2136 updates and TXT record lookup.
|
||||
|
||||
package server
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type rfc2136Handler struct {
|
||||
log logr.Logger
|
||||
|
||||
txtRecords map[string][]string
|
||||
zones []string
|
||||
tsigZone string
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// serveDNS implements github.com/miekg/dns.Handler
|
||||
func (b *rfc2136Handler) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
log := b.log.WithName("serveDNS")
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(req)
|
||||
defer w.WriteMsg(m)
|
||||
|
||||
var zone string
|
||||
if len(req.Question) > 0 {
|
||||
question := req.Question[0].Name
|
||||
log = log.WithValues("question", question, "opcode", dns.OpcodeToString[req.Opcode])
|
||||
zone = b.zoneForFQDN(question)
|
||||
if zone == "" {
|
||||
log.Info("failed to lookup zone for fqdn")
|
||||
m.Rcode = dns.RcodeServerFailure
|
||||
return
|
||||
}
|
||||
log = log.WithValues("zone", zone)
|
||||
}
|
||||
|
||||
if t := req.IsTsig(); t != nil {
|
||||
log.Info("TSIG requested on DNS request")
|
||||
if w.TsigStatus() == nil {
|
||||
log.Info("setting TSIG values on response")
|
||||
// Validated
|
||||
m.SetTsig(b.tsigZone, dns.HmacMD5, 300, time.Now().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
// updates are currently accepted for *all* zones
|
||||
if req.Opcode == dns.OpcodeUpdate {
|
||||
for _, rr := range req.Ns {
|
||||
txt := rr.(*dns.TXT)
|
||||
log := log.WithValues("value", txt.Hdr.Name, "class", dns.ClassToString[rr.Header().Class], "txt", txt.Txt)
|
||||
if rr.Header().Class == dns.ClassNONE {
|
||||
log.Info("deleting txt record value due to NONE class")
|
||||
// TODO: can we only delete the named record here somehow?
|
||||
delete(b.txtRecords, txt.Hdr.Name)
|
||||
continue
|
||||
}
|
||||
log.Info("setting TXT record value")
|
||||
b.txtRecords[txt.Hdr.Name] = txt.Txt
|
||||
}
|
||||
}
|
||||
|
||||
switch req.Question[0].Qtype {
|
||||
case dns.TypeSOA:
|
||||
// Return SOA to appease findZoneByFqdn()
|
||||
soaRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN SOA ns1.%s admin.%s 2016022801 28800 7200 2419200 1200", zone, defaultTTL, zone, zone))
|
||||
m.Answer = []dns.RR{soaRR}
|
||||
case dns.TypeTXT:
|
||||
for _, rr := range b.txtRecords[req.Question[0].Name] {
|
||||
txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", req.Question[0].Name, defaultTTL, rr))
|
||||
m.Answer = append(m.Answer, txtRR)
|
||||
}
|
||||
}
|
||||
|
||||
for _, rr := range m.Answer {
|
||||
log.Info("responding", "response", rr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *rfc2136Handler) zoneForFQDN(s string) string {
|
||||
for _, z := range b.zones {
|
||||
if dns.IsSubDomain(z, s) {
|
||||
return z
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTTL = 60
|
||||
)
|
||||
|
||||
type BasicServer struct {
|
||||
// Zones is a list of DNS zones that this server should accept responses
|
||||
// for.
|
||||
Zones []string
|
||||
|
||||
// Handler is an optional
|
||||
Handler dns.Handler
|
||||
|
||||
// TSIG configuration options
|
||||
// EnableTSIG enables TSIG support for the DNS server
|
||||
// If true, both TSIGKeyName and TSIGKeySecret must be provided.
|
||||
EnableTSIG bool
|
||||
// TSIGKeyName to be used in responses when TSIG is enabled
|
||||
TSIGKeyName string
|
||||
// TSIGKeySecret to be used in responses when TSIG is enabled
|
||||
TSIGKeySecret string
|
||||
// TSIGZone is the DNS zone that should be used in TSIG responses
|
||||
TSIGZone string
|
||||
|
||||
ctx context.Context
|
||||
listenAddr string
|
||||
server *dns.Server
|
||||
}
|
||||
|
||||
// Run starts the test DNS server, binding to a random port on 127.0.0.1
|
||||
func (b *BasicServer) Run(ctx context.Context) error {
|
||||
return b.RunWithAddress(ctx, "127.0.0.1:0")
|
||||
}
|
||||
|
||||
// RunWithAddress starts the test DNS server using the specified listen address.
|
||||
func (b *BasicServer) RunWithAddress(ctx context.Context, listenAddr string) error {
|
||||
log := logf.FromContext(ctx, "dnsBasicServer")
|
||||
|
||||
if listenAddr == "" {
|
||||
return fmt.Errorf("listen address must be provided")
|
||||
}
|
||||
|
||||
pc, err := net.ListenPacket("udp", listenAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.listenAddr = pc.LocalAddr().String()
|
||||
log = log.WithValues("address", b.listenAddr)
|
||||
log.Info("listening on UDP port")
|
||||
|
||||
// update the ctx with the new logger
|
||||
ctx = logf.NewContext(ctx, log)
|
||||
|
||||
b.server = &dns.Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
|
||||
if b.EnableTSIG {
|
||||
log.Info("enabling TSIG support")
|
||||
b.server.TsigSecret = map[string]string{b.TSIGKeyName: b.TSIGKeySecret}
|
||||
}
|
||||
|
||||
if b.Handler == nil {
|
||||
b.Handler = &rfc2136Handler{
|
||||
log: log,
|
||||
txtRecords: make(map[string][]string),
|
||||
zones: b.Zones,
|
||||
tsigZone: b.TSIGZone,
|
||||
}
|
||||
}
|
||||
b.server.Handler = b.Handler
|
||||
|
||||
// Start the DNS server in a separate goroutine and wait for it to start
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
b.server.NotifyStartedFunc = waitLock.Unlock
|
||||
go func() {
|
||||
log.Info("starting DNS server")
|
||||
b.server.ActivateAndServe()
|
||||
log.Info("DNS server exited")
|
||||
pc.Close()
|
||||
}()
|
||||
waitLock.Lock()
|
||||
defer waitLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BasicServer) ListenAddr() string {
|
||||
return b.listenAddr
|
||||
}
|
||||
|
||||
func (b *BasicServer) Shutdown() error {
|
||||
return b.server.Shutdown()
|
||||
}
|
||||
Reference in New Issue
Block a user