mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-18 12:37:02 +02:00
This allows it to authenticate once, then exit once all sinks have reported success. Useful for things like an init container vs. a sidecard container. Also adds command-level testing of it.
240 lines
5.3 KiB
Go
240 lines
5.3 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/errwrap"
|
|
log "github.com/hashicorp/go-hclog"
|
|
multierror "github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/vault/helper/parseutil"
|
|
|
|
"github.com/hashicorp/hcl"
|
|
"github.com/hashicorp/hcl/hcl/ast"
|
|
)
|
|
|
|
// Config is the configuration for the vault server.
|
|
type Config struct {
|
|
AutoAuth *AutoAuth `hcl:"auto_auth"`
|
|
ExitAfterAuth bool `hcl:"exit_after_auth"`
|
|
PidFile string `hcl:"pid_file"`
|
|
}
|
|
|
|
type AutoAuth struct {
|
|
Method *Method `hcl:"-"`
|
|
Sinks []*Sink `hcl:"sinks"`
|
|
}
|
|
|
|
type Method struct {
|
|
Type string
|
|
MountPath string `hcl:"mount_path"`
|
|
WrapTTLRaw interface{} `hcl:"wrap_ttl"`
|
|
WrapTTL time.Duration `hcl:"-"`
|
|
Config map[string]interface{}
|
|
}
|
|
|
|
type Sink struct {
|
|
Type string
|
|
WrapTTLRaw interface{} `hcl:"wrap_ttl"`
|
|
WrapTTL time.Duration `hcl:"-"`
|
|
DHType string `hcl:"dh_type"`
|
|
DHPath string `hcl:"dh_path"`
|
|
AAD string `hcl:"aad"`
|
|
AADEnvVar string `hcl:"aad_env_var"`
|
|
Config map[string]interface{}
|
|
}
|
|
|
|
// LoadConfig loads the configuration at the given path, regardless if
|
|
// its a file or directory.
|
|
func LoadConfig(path string, logger log.Logger) (*Config, error) {
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if fi.IsDir() {
|
|
return nil, fmt.Errorf("location is a directory, not a file")
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
list, ok := obj.Node.(*ast.ObjectList)
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
|
|
}
|
|
|
|
if err := parseAutoAuth(&result, list); err != nil {
|
|
return nil, errwrap.Wrapf("error parsing 'auto_auth': {{err}}", err)
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
func parseAutoAuth(result *Config, list *ast.ObjectList) error {
|
|
name := "auto_auth"
|
|
|
|
autoAuthList := list.Filter(name)
|
|
if len(autoAuthList.Items) != 1 {
|
|
return fmt.Errorf("one and only one %q block is required", name)
|
|
}
|
|
|
|
// Get our item
|
|
item := autoAuthList.Items[0]
|
|
|
|
var a AutoAuth
|
|
if err := hcl.DecodeObject(&a, item.Val); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.AutoAuth = &a
|
|
|
|
subs, ok := item.Val.(*ast.ObjectType)
|
|
if !ok {
|
|
return fmt.Errorf("could not parse %q as an object", name)
|
|
}
|
|
subList := subs.List
|
|
|
|
if err := parseMethod(result, subList); err != nil {
|
|
return errwrap.Wrapf("error parsing 'method': {{err}}", err)
|
|
}
|
|
|
|
if err := parseSinks(result, subList); err != nil {
|
|
return errwrap.Wrapf("error parsing 'sink' stanzas: {{err}}", err)
|
|
}
|
|
|
|
switch {
|
|
case a.Method == nil:
|
|
return fmt.Errorf("no 'method' block found")
|
|
case len(a.Sinks) == 0:
|
|
return fmt.Errorf("at least one 'sink' block must be provided")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseMethod(result *Config, list *ast.ObjectList) error {
|
|
name := "method"
|
|
|
|
methodList := list.Filter(name)
|
|
if len(methodList.Items) != 1 {
|
|
return fmt.Errorf("one and only one %q block is required", name)
|
|
}
|
|
|
|
// Get our item
|
|
item := methodList.Items[0]
|
|
|
|
var m Method
|
|
if err := hcl.DecodeObject(&m, item.Val); err != nil {
|
|
return err
|
|
}
|
|
|
|
if m.Type == "" {
|
|
if len(item.Keys) == 1 {
|
|
m.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
|
|
}
|
|
if m.Type == "" {
|
|
return errors.New("method type must be specified")
|
|
}
|
|
}
|
|
|
|
// Default to Vault's default
|
|
if m.MountPath == "" {
|
|
m.MountPath = fmt.Sprintf("auth/%s", m.Type)
|
|
}
|
|
// Standardize on no trailing slash
|
|
m.MountPath = strings.TrimSuffix(m.MountPath, "/")
|
|
|
|
if m.WrapTTLRaw != nil {
|
|
var err error
|
|
if m.WrapTTL, err = parseutil.ParseDurationSecond(m.WrapTTLRaw); err != nil {
|
|
return err
|
|
}
|
|
m.WrapTTLRaw = nil
|
|
}
|
|
|
|
result.AutoAuth.Method = &m
|
|
return nil
|
|
}
|
|
|
|
func parseSinks(result *Config, list *ast.ObjectList) error {
|
|
name := "sink"
|
|
|
|
sinkList := list.Filter(name)
|
|
if len(sinkList.Items) < 1 {
|
|
return fmt.Errorf("at least one %q block is required", name)
|
|
}
|
|
|
|
var ts []*Sink
|
|
|
|
for _, item := range sinkList.Items {
|
|
var s Sink
|
|
if err := hcl.DecodeObject(&s, item.Val); err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.Type == "" {
|
|
if len(item.Keys) == 1 {
|
|
s.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
|
|
}
|
|
if s.Type == "" {
|
|
return errors.New("sink type must be specified")
|
|
}
|
|
}
|
|
|
|
if s.WrapTTLRaw != nil {
|
|
var err error
|
|
if s.WrapTTL, err = parseutil.ParseDurationSecond(s.WrapTTLRaw); err != nil {
|
|
return multierror.Prefix(err, fmt.Sprintf("sink.%s", s.Type))
|
|
}
|
|
s.WrapTTLRaw = nil
|
|
}
|
|
|
|
switch s.DHType {
|
|
case "":
|
|
case "curve25519":
|
|
default:
|
|
return multierror.Prefix(errors.New("invalid value for 'dh_type'"), fmt.Sprintf("sink.%s", s.Type))
|
|
}
|
|
|
|
if s.AADEnvVar != "" {
|
|
s.AAD = os.Getenv(s.AADEnvVar)
|
|
s.AADEnvVar = ""
|
|
}
|
|
|
|
switch {
|
|
case s.DHPath == "" && s.DHType == "":
|
|
if s.AAD != "" {
|
|
return multierror.Prefix(errors.New("specifying AAD data without 'dh_type' does not make sense"), fmt.Sprintf("sink.%s", s.Type))
|
|
}
|
|
case s.DHPath != "" && s.DHType != "":
|
|
default:
|
|
return multierror.Prefix(errors.New("'dh_type' and 'dh_path' must be specified together"), fmt.Sprintf("sink.%s", s.Type))
|
|
}
|
|
|
|
ts = append(ts, &s)
|
|
}
|
|
|
|
result.AutoAuth.Sinks = ts
|
|
return nil
|
|
}
|