mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	* expose tsig secrets via dnsserver.Config * add tsig plugin Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
		
			
				
	
	
		
			169 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package tsig
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/coredns/caddy"
 | |
| 	"github.com/coredns/coredns/core/dnsserver"
 | |
| 	"github.com/coredns/coredns/plugin"
 | |
| 
 | |
| 	"github.com/miekg/dns"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	caddy.RegisterPlugin(pluginName, caddy.Plugin{
 | |
| 		ServerType: "dns",
 | |
| 		Action:     setup,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func setup(c *caddy.Controller) error {
 | |
| 	t, err := parse(c)
 | |
| 	if err != nil {
 | |
| 		return plugin.Error(pluginName, c.ArgErr())
 | |
| 	}
 | |
| 
 | |
| 	config := dnsserver.GetConfig(c)
 | |
| 
 | |
| 	config.TsigSecret = t.secrets
 | |
| 
 | |
| 	config.AddPlugin(func(next plugin.Handler) plugin.Handler {
 | |
| 		t.Next = next
 | |
| 		return t
 | |
| 	})
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func parse(c *caddy.Controller) (*TSIGServer, error) {
 | |
| 	t := &TSIGServer{
 | |
| 		secrets: make(map[string]string),
 | |
| 		types:   defaultQTypes,
 | |
| 	}
 | |
| 
 | |
| 	for i := 0; c.Next(); i++ {
 | |
| 		if i > 0 {
 | |
| 			return nil, plugin.ErrOnce
 | |
| 		}
 | |
| 
 | |
| 		t.Zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys)
 | |
| 		for c.NextBlock() {
 | |
| 			switch c.Val() {
 | |
| 			case "secret":
 | |
| 				args := c.RemainingArgs()
 | |
| 				if len(args) != 2 {
 | |
| 					return nil, c.ArgErr()
 | |
| 				}
 | |
| 				k := plugin.Name(args[0]).Normalize()
 | |
| 				if _, exists := t.secrets[k]; exists {
 | |
| 					return nil, fmt.Errorf("key %q redefined", k)
 | |
| 				}
 | |
| 				t.secrets[k] = args[1]
 | |
| 			case "secrets":
 | |
| 				args := c.RemainingArgs()
 | |
| 				if len(args) != 1 {
 | |
| 					return nil, c.ArgErr()
 | |
| 				}
 | |
| 				f, err := os.Open(args[0])
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				secrets, err := parseKeyFile(f)
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				for k, s := range secrets {
 | |
| 					if _, exists := t.secrets[k]; exists {
 | |
| 						return nil, fmt.Errorf("key %q redefined", k)
 | |
| 					}
 | |
| 					t.secrets[k] = s
 | |
| 				}
 | |
| 			case "require":
 | |
| 				t.types = qTypes{}
 | |
| 				args := c.RemainingArgs()
 | |
| 				if len(args) == 0 {
 | |
| 					return nil, c.ArgErr()
 | |
| 				}
 | |
| 				if args[0] == "all" {
 | |
| 					t.all = true
 | |
| 					continue
 | |
| 				}
 | |
| 				if args[0] == "none" {
 | |
| 					continue
 | |
| 				}
 | |
| 				for _, str := range args {
 | |
| 					qt, ok := dns.StringToType[str]
 | |
| 					if !ok {
 | |
| 						return nil, c.Errf("unknown query type '%s'", str)
 | |
| 					}
 | |
| 					t.types[qt] = struct{}{}
 | |
| 				}
 | |
| 			default:
 | |
| 				return nil, c.Errf("unknown property '%s'", c.Val())
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return t, nil
 | |
| }
 | |
| 
 | |
| func parseKeyFile(f io.Reader) (map[string]string, error) {
 | |
| 	secrets := make(map[string]string)
 | |
| 	s := bufio.NewScanner(f)
 | |
| 	for s.Scan() {
 | |
| 		fields := strings.Fields(s.Text())
 | |
| 		if len(fields) == 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if fields[0] != "key" {
 | |
| 			return nil, fmt.Errorf("unexpected token %q", fields[0])
 | |
| 		}
 | |
| 		if len(fields) < 2 {
 | |
| 			return nil, fmt.Errorf("expected key name %q", s.Text())
 | |
| 		}
 | |
| 		key := strings.Trim(fields[1], "\"{")
 | |
| 		if len(key) == 0 {
 | |
| 			return nil, fmt.Errorf("expected key name %q", s.Text())
 | |
| 		}
 | |
| 		key = plugin.Name(key).Normalize()
 | |
| 		if _, ok := secrets[key]; ok {
 | |
| 			return nil, fmt.Errorf("key %q redefined", key)
 | |
| 		}
 | |
| 	key:
 | |
| 		for s.Scan() {
 | |
| 			fields := strings.Fields(s.Text())
 | |
| 			if len(fields) == 0 {
 | |
| 				continue
 | |
| 			}
 | |
| 			switch fields[0] {
 | |
| 			case "algorithm":
 | |
| 				continue
 | |
| 			case "secret":
 | |
| 				if len(fields) < 2 {
 | |
| 					return nil, fmt.Errorf("expected secret key %q", s.Text())
 | |
| 				}
 | |
| 				secret := strings.Trim(fields[1], "\";")
 | |
| 				if len(secret) == 0 {
 | |
| 					return nil, fmt.Errorf("expected secret key %q", s.Text())
 | |
| 				}
 | |
| 				secrets[key] = secret
 | |
| 			case "}":
 | |
| 				fallthrough
 | |
| 			case "};":
 | |
| 				break key
 | |
| 			default:
 | |
| 				return nil, fmt.Errorf("unexpected token %q", fields[0])
 | |
| 			}
 | |
| 		}
 | |
| 		if _, ok := secrets[key]; !ok {
 | |
| 			return nil, fmt.Errorf("expected secret for key %q", key)
 | |
| 		}
 | |
| 	}
 | |
| 	return secrets, nil
 | |
| }
 | |
| 
 | |
| var defaultQTypes = qTypes{}
 |