mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	plugin/tsig: new plugin TSIG (#4957)
* expose tsig secrets via dnsserver.Config * add tsig plugin Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
		
							
								
								
									
										168
									
								
								plugin/tsig/setup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								plugin/tsig/setup.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| 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{} | ||||
		Reference in New Issue
	
	Block a user