mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 20:17:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			319 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package server
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/hashicorp/hcl"
 | 
						|
	hclobj "github.com/hashicorp/hcl/hcl"
 | 
						|
)
 | 
						|
 | 
						|
// Config is the configuration for the vault server.
 | 
						|
type Config struct {
 | 
						|
	Listeners []*Listener `hcl:"-"`
 | 
						|
	Backend   *Backend    `hcl:"-"`
 | 
						|
 | 
						|
	StatsiteAddr string `hcl:"statsite_addr"`
 | 
						|
	StatsdAddr   string `hcl:"statsd_addr"`
 | 
						|
}
 | 
						|
 | 
						|
// DevConfig is a Config that is used for dev mode of Vault.
 | 
						|
func DevConfig() *Config {
 | 
						|
	return &Config{
 | 
						|
		Backend: &Backend{
 | 
						|
			Type: "inmem",
 | 
						|
		},
 | 
						|
 | 
						|
		Listeners: []*Listener{
 | 
						|
			&Listener{
 | 
						|
				Type: "tcp",
 | 
						|
				Config: map[string]string{
 | 
						|
					"tls_disable": "1",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Listener is the listener configuration for the server.
 | 
						|
type Listener struct {
 | 
						|
	Type   string
 | 
						|
	Config map[string]string
 | 
						|
}
 | 
						|
 | 
						|
func (l *Listener) GoString() string {
 | 
						|
	return fmt.Sprintf("*%#v", *l)
 | 
						|
}
 | 
						|
 | 
						|
// Backend is the backend configuration for the server.
 | 
						|
type Backend struct {
 | 
						|
	Type          string
 | 
						|
	AdvertiseAddr string
 | 
						|
	Config        map[string]string
 | 
						|
}
 | 
						|
 | 
						|
func (b *Backend) GoString() string {
 | 
						|
	return fmt.Sprintf("*%#v", *b)
 | 
						|
}
 | 
						|
 | 
						|
// Merge merges two configurations.
 | 
						|
func (c *Config) Merge(c2 *Config) *Config {
 | 
						|
	result := new(Config)
 | 
						|
	for _, l := range c.Listeners {
 | 
						|
		result.Listeners = append(result.Listeners, l)
 | 
						|
	}
 | 
						|
	for _, l := range c2.Listeners {
 | 
						|
		result.Listeners = append(result.Listeners, l)
 | 
						|
	}
 | 
						|
 | 
						|
	result.Backend = c.Backend
 | 
						|
	if c2.Backend != nil {
 | 
						|
		result.Backend = c2.Backend
 | 
						|
	}
 | 
						|
 | 
						|
	if c2.StatsiteAddr != "" {
 | 
						|
		result.StatsiteAddr = c2.StatsiteAddr
 | 
						|
	}
 | 
						|
	if c2.StatsdAddr != "" {
 | 
						|
		result.StatsdAddr = c2.StatsdAddr
 | 
						|
	}
 | 
						|
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
// LoadConfig loads the configuration at the given path, regardless if
 | 
						|
// its a file or directory.
 | 
						|
func LoadConfig(path string) (*Config, error) {
 | 
						|
	fi, err := os.Stat(path)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if fi.IsDir() {
 | 
						|
		return LoadConfigDir(path)
 | 
						|
	} else {
 | 
						|
		return LoadConfigFile(path)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// LoadConfigFile loads the configuration from the given file.
 | 
						|
func LoadConfigFile(path string) (*Config, error) {
 | 
						|
	// Read the file
 | 
						|
	d, err := ioutil.ReadFile(path)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Parse!
 | 
						|
	obj, err := hcl.Parse(string(d))
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Start building the result
 | 
						|
	var result Config
 | 
						|
	if err := hcl.DecodeObject(&result, obj); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if objs := obj.Get("listener", false); objs != nil {
 | 
						|
		result.Listeners, err = loadListeners(objs)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if objs := obj.Get("backend", false); objs != nil {
 | 
						|
		result.Backend, err = loadBackend(objs)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return &result, nil
 | 
						|
}
 | 
						|
 | 
						|
// LoadConfigDir loads all the configurations in the given directory
 | 
						|
// in alphabetical order.
 | 
						|
func LoadConfigDir(dir string) (*Config, error) {
 | 
						|
	f, err := os.Open(dir)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
 | 
						|
	fi, err := f.Stat()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if !fi.IsDir() {
 | 
						|
		return nil, fmt.Errorf(
 | 
						|
			"configuration path must be a directory: %s",
 | 
						|
			dir)
 | 
						|
	}
 | 
						|
 | 
						|
	var files []string
 | 
						|
	err = nil
 | 
						|
	for err != io.EOF {
 | 
						|
		var fis []os.FileInfo
 | 
						|
		fis, err = f.Readdir(128)
 | 
						|
		if err != nil && err != io.EOF {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		for _, fi := range fis {
 | 
						|
			// Ignore directories
 | 
						|
			if fi.IsDir() {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			// Only care about files that are valid to load.
 | 
						|
			name := fi.Name()
 | 
						|
			skip := true
 | 
						|
			if strings.HasSuffix(name, ".hcl") {
 | 
						|
				skip = false
 | 
						|
			} else if strings.HasSuffix(name, ".json") {
 | 
						|
				skip = false
 | 
						|
			}
 | 
						|
			if skip || isTemporaryFile(name) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			path := filepath.Join(dir, name)
 | 
						|
			files = append(files, path)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var result *Config
 | 
						|
	for _, f := range files {
 | 
						|
		config, err := LoadConfigFile(f)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("Error loading %s: %s", f, err)
 | 
						|
		}
 | 
						|
 | 
						|
		if result == nil {
 | 
						|
			result = config
 | 
						|
		} else {
 | 
						|
			result = result.Merge(config)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return result, nil
 | 
						|
}
 | 
						|
 | 
						|
// isTemporaryFile returns true or false depending on whether the
 | 
						|
// provided file name is a temporary file for the following editors:
 | 
						|
// emacs or vim.
 | 
						|
func isTemporaryFile(name string) bool {
 | 
						|
	return strings.HasSuffix(name, "~") || // vim
 | 
						|
		strings.HasPrefix(name, ".#") || // emacs
 | 
						|
		(strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs
 | 
						|
}
 | 
						|
 | 
						|
func loadListeners(os *hclobj.Object) ([]*Listener, error) {
 | 
						|
	var allNames []*hclobj.Object
 | 
						|
 | 
						|
	// Really confusing iteration. The key is the false/true parameter
 | 
						|
	// of whether we're expanding or not. We first iterate over all
 | 
						|
	// the "listeners"
 | 
						|
	for _, o1 := range os.Elem(false) {
 | 
						|
		// Iterate expand to get the list of types
 | 
						|
		for _, o2 := range o1.Elem(true) {
 | 
						|
			switch o2.Type {
 | 
						|
			case hclobj.ValueTypeList:
 | 
						|
				// This switch is for JSON, to allow them to do this:
 | 
						|
				//
 | 
						|
				// "tcp": [{ ... }, { ... }]
 | 
						|
				//
 | 
						|
				// To configure multiple listeners of the same type.
 | 
						|
				for _, o3 := range o2.Elem(true) {
 | 
						|
					o3.Key = o2.Key
 | 
						|
					allNames = append(allNames, o3)
 | 
						|
				}
 | 
						|
			case hclobj.ValueTypeObject:
 | 
						|
				// This is for the standard `listener "tcp" { ... }` syntax
 | 
						|
				allNames = append(allNames, o2)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(allNames) == 0 {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Now go over all the types and their children in order to get
 | 
						|
	// all of the actual resources.
 | 
						|
	result := make([]*Listener, 0, len(allNames))
 | 
						|
	for _, obj := range allNames {
 | 
						|
		k := obj.Key
 | 
						|
 | 
						|
		var config map[string]string
 | 
						|
		if err := hcl.DecodeObject(&config, obj); err != nil {
 | 
						|
			return nil, fmt.Errorf(
 | 
						|
				"Error reading config for %s: %s",
 | 
						|
				k,
 | 
						|
				err)
 | 
						|
		}
 | 
						|
 | 
						|
		result = append(result, &Listener{
 | 
						|
			Type:   k,
 | 
						|
			Config: config,
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return result, nil
 | 
						|
}
 | 
						|
 | 
						|
func loadBackend(os *hclobj.Object) (*Backend, error) {
 | 
						|
	var allNames []*hclobj.Object
 | 
						|
 | 
						|
	// See loadListeners
 | 
						|
	for _, o1 := range os.Elem(false) {
 | 
						|
		// Iterate expand to get the list of types
 | 
						|
		for _, o2 := range o1.Elem(true) {
 | 
						|
			// Iterate non-expand to get the full list of types
 | 
						|
			for _, o3 := range o2.Elem(false) {
 | 
						|
				allNames = append(allNames, o3)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(allNames) == 0 {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	if len(allNames) > 1 {
 | 
						|
		keys := make([]string, 0, len(allNames))
 | 
						|
		for _, o := range allNames {
 | 
						|
			keys = append(keys, o.Key)
 | 
						|
		}
 | 
						|
 | 
						|
		return nil, fmt.Errorf(
 | 
						|
			"Multiple backends declared. Only one is allowed: %v", keys)
 | 
						|
	}
 | 
						|
 | 
						|
	// Now go over all the types and their children in order to get
 | 
						|
	// all of the actual resources.
 | 
						|
	var result Backend
 | 
						|
	obj := allNames[0]
 | 
						|
	result.Type = obj.Key
 | 
						|
 | 
						|
	var config map[string]string
 | 
						|
	if err := hcl.DecodeObject(&config, obj); err != nil {
 | 
						|
		return nil, fmt.Errorf(
 | 
						|
			"Error reading config for backend %s: %s",
 | 
						|
			result.Type,
 | 
						|
			err)
 | 
						|
	}
 | 
						|
 | 
						|
	if v, ok := config["advertise_addr"]; ok {
 | 
						|
		result.AdvertiseAddr = v
 | 
						|
		delete(config, "advertise_addr")
 | 
						|
	}
 | 
						|
 | 
						|
	result.Config = config
 | 
						|
	return &result, nil
 | 
						|
}
 |