mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-02 10:13:14 -05:00 
			
		
		
		
	
		
			
	
	
		
			141 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			141 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package tsig
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"context"
							 | 
						||
| 
								 | 
							
									"encoding/binary"
							 | 
						||
| 
								 | 
							
									"encoding/hex"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/coredns/coredns/plugin"
							 | 
						||
| 
								 | 
							
									"github.com/coredns/coredns/plugin/pkg/log"
							 | 
						||
| 
								 | 
							
									"github.com/coredns/coredns/request"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/miekg/dns"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// TSIGServer verifies tsig status and adds tsig to responses
							 | 
						||
| 
								 | 
							
								type TSIGServer struct {
							 | 
						||
| 
								 | 
							
									Zones   []string
							 | 
						||
| 
								 | 
							
									secrets map[string]string // [key-name]secret
							 | 
						||
| 
								 | 
							
									types   qTypes
							 | 
						||
| 
								 | 
							
									all     bool
							 | 
						||
| 
								 | 
							
									Next    plugin.Handler
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type qTypes map[uint16]struct{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Name implements plugin.Handler
							 | 
						||
| 
								 | 
							
								func (t TSIGServer) Name() string { return pluginName }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ServeDNS implements plugin.Handler
							 | 
						||
| 
								 | 
							
								func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
							 | 
						||
| 
								 | 
							
									var err error
							 | 
						||
| 
								 | 
							
									state := request.Request{Req: r, W: w}
							 | 
						||
| 
								 | 
							
									if z := plugin.Zones(t.Zones).Matches(state.Name()); z == "" {
							 | 
						||
| 
								 | 
							
										return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var tsigRR = r.IsTsig()
							 | 
						||
| 
								 | 
							
									rcode := dns.RcodeSuccess
							 | 
						||
| 
								 | 
							
									if !t.tsigRequired(state.QType()) && tsigRR == nil {
							 | 
						||
| 
								 | 
							
										return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if tsigRR == nil {
							 | 
						||
| 
								 | 
							
										log.Debugf("rejecting '%s' request without TSIG\n", dns.TypeToString[state.QType()])
							 | 
						||
| 
								 | 
							
										rcode = dns.RcodeRefused
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// wrap the response writer so the response will be TSIG signed.
							 | 
						||
| 
								 | 
							
									w = &restoreTsigWriter{w, r, tsigRR}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									tsigStatus := w.TsigStatus()
							 | 
						||
| 
								 | 
							
									if tsigStatus != nil {
							 | 
						||
| 
								 | 
							
										log.Debugf("TSIG validation failed: %v %v", dns.TypeToString[state.QType()], tsigStatus)
							 | 
						||
| 
								 | 
							
										rcode = dns.RcodeNotAuth
							 | 
						||
| 
								 | 
							
										switch tsigStatus {
							 | 
						||
| 
								 | 
							
										case dns.ErrSecret:
							 | 
						||
| 
								 | 
							
											tsigRR.Error = dns.RcodeBadKey
							 | 
						||
| 
								 | 
							
										case dns.ErrTime:
							 | 
						||
| 
								 | 
							
											tsigRR.Error = dns.RcodeBadTime
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											tsigRR.Error = dns.RcodeBadSig
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										resp := new(dns.Msg).SetRcode(r, rcode)
							 | 
						||
| 
								 | 
							
										w.WriteMsg(resp)
							 | 
						||
| 
								 | 
							
										return dns.RcodeSuccess, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// strip the TSIG RR. Next, and subsequent plugins will not see the TSIG RRs.
							 | 
						||
| 
								 | 
							
									// This violates forwarding cases (RFC 8945 5.5). See README.md Bugs
							 | 
						||
| 
								 | 
							
									if len(r.Extra) > 1 {
							 | 
						||
| 
								 | 
							
										r.Extra = r.Extra[0 : len(r.Extra)-1]
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										r.Extra = []dns.RR{}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if rcode == dns.RcodeSuccess {
							 | 
						||
| 
								 | 
							
										rcode, err = plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											log.Errorf("request handler returned an error: %v\n", err)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// If the plugin chain result was not an error, restore the TSIG and write the response.
							 | 
						||
| 
								 | 
							
									if !plugin.ClientWrite(rcode) {
							 | 
						||
| 
								 | 
							
										resp := new(dns.Msg).SetRcode(r, rcode)
							 | 
						||
| 
								 | 
							
										w.WriteMsg(resp)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return dns.RcodeSuccess, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (t *TSIGServer) tsigRequired(qtype uint16) bool {
							 | 
						||
| 
								 | 
							
									if t.all {
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if _, ok := t.types[qtype]; ok {
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// restoreTsigWriter Implement Response Writer, and adds a TSIG RR to a response
							 | 
						||
| 
								 | 
							
								type restoreTsigWriter struct {
							 | 
						||
| 
								 | 
							
									dns.ResponseWriter
							 | 
						||
| 
								 | 
							
									req     *dns.Msg  // original request excluding TSIG if it has one
							 | 
						||
| 
								 | 
							
									reqTSIG *dns.TSIG // original TSIG
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// WriteMsg adds a TSIG RR to the response
							 | 
						||
| 
								 | 
							
								func (r *restoreTsigWriter) WriteMsg(m *dns.Msg) error {
							 | 
						||
| 
								 | 
							
									// Make sure the response has an EDNS OPT RR if the request had it.
							 | 
						||
| 
								 | 
							
									// Otherwise ScrubWriter would append it *after* TSIG, making it a non-compliant DNS message.
							 | 
						||
| 
								 | 
							
									state := request.Request{Req: r.req, W: r.ResponseWriter}
							 | 
						||
| 
								 | 
							
									state.SizeAndDo(m)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									repTSIG := m.IsTsig()
							 | 
						||
| 
								 | 
							
									if r.reqTSIG != nil && repTSIG == nil {
							 | 
						||
| 
								 | 
							
										repTSIG = new(dns.TSIG)
							 | 
						||
| 
								 | 
							
										repTSIG.Hdr = dns.RR_Header{Name: r.reqTSIG.Hdr.Name, Rrtype: dns.TypeTSIG, Class: dns.ClassANY}
							 | 
						||
| 
								 | 
							
										repTSIG.Algorithm = r.reqTSIG.Algorithm
							 | 
						||
| 
								 | 
							
										repTSIG.OrigId = m.MsgHdr.Id
							 | 
						||
| 
								 | 
							
										repTSIG.Error = r.reqTSIG.Error
							 | 
						||
| 
								 | 
							
										repTSIG.MAC = r.reqTSIG.MAC
							 | 
						||
| 
								 | 
							
										repTSIG.MACSize = r.reqTSIG.MACSize
							 | 
						||
| 
								 | 
							
										if repTSIG.Error == dns.RcodeBadTime {
							 | 
						||
| 
								 | 
							
											// per RFC 8945 5.2.3. client time goes into TimeSigned, server time in OtherData, OtherLen = 6 ...
							 | 
						||
| 
								 | 
							
											repTSIG.TimeSigned = r.reqTSIG.TimeSigned
							 | 
						||
| 
								 | 
							
											b := make([]byte, 8)
							 | 
						||
| 
								 | 
							
											// TimeSigned is network byte order.
							 | 
						||
| 
								 | 
							
											binary.BigEndian.PutUint64(b, uint64(time.Now().Unix()))
							 | 
						||
| 
								 | 
							
											// truncate to 48 least significant bits (network order 6 rightmost bytes)
							 | 
						||
| 
								 | 
							
											repTSIG.OtherData = hex.EncodeToString(b[2:])
							 | 
						||
| 
								 | 
							
											repTSIG.OtherLen = 6
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m.Extra = append(m.Extra, repTSIG)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return r.ResponseWriter.WriteMsg(m)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const pluginName = "tsig"
							 |