* root: fix config loading for outposts Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix error handling Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve check to see if outpost is embedded or not Signed-off-by: Jens Langhammer <jens@goauthentik.io> * also fix oauth url fetching Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
		
			
				
	
	
		
			205 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package config
 | 
						|
 | 
						|
import (
 | 
						|
	_ "embed"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/url"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	env "github.com/Netflix/go-env"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
	"goauthentik.io/authentik/lib"
 | 
						|
	"gopkg.in/yaml.v2"
 | 
						|
)
 | 
						|
 | 
						|
var cfg *Config
 | 
						|
 | 
						|
const defaultConfigPath = "./authentik/lib/default.yml"
 | 
						|
 | 
						|
func getConfigPaths() []string {
 | 
						|
	configPaths := []string{defaultConfigPath, "/etc/authentik/config.yml", ""}
 | 
						|
	globConfigPaths, _ := filepath.Glob("/etc/authentik/config.d/*.yml")
 | 
						|
	configPaths = append(configPaths, globConfigPaths...)
 | 
						|
 | 
						|
	environment := "local"
 | 
						|
	if v, ok := os.LookupEnv("AUTHENTIK_ENV"); ok {
 | 
						|
		environment = v
 | 
						|
	}
 | 
						|
 | 
						|
	computedConfigPaths := []string{}
 | 
						|
 | 
						|
	for _, path := range configPaths {
 | 
						|
		path, err := filepath.Abs(path)
 | 
						|
		if err != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if stat, err := os.Stat(path); err == nil {
 | 
						|
			if !stat.IsDir() {
 | 
						|
				computedConfigPaths = append(computedConfigPaths, path)
 | 
						|
			} else {
 | 
						|
				envPaths := []string{
 | 
						|
					filepath.Join(path, environment+".yml"),
 | 
						|
					filepath.Join(path, environment+".env.yml"),
 | 
						|
				}
 | 
						|
				for _, envPath := range envPaths {
 | 
						|
					if stat, err = os.Stat(envPath); err == nil && !stat.IsDir() {
 | 
						|
						computedConfigPaths = append(computedConfigPaths, envPath)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return computedConfigPaths
 | 
						|
}
 | 
						|
 | 
						|
func Get() *Config {
 | 
						|
	if cfg == nil {
 | 
						|
		c := &Config{}
 | 
						|
		c.Setup(getConfigPaths()...)
 | 
						|
		cfg = c
 | 
						|
	}
 | 
						|
	return cfg
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) Setup(paths ...string) {
 | 
						|
	// initially try to load the default config which is compiled in
 | 
						|
	err := c.LoadConfig(lib.DefaultConfig())
 | 
						|
	// this should never fail
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Errorf("failed to load inbuilt config: %v", err))
 | 
						|
	}
 | 
						|
	log.WithField("path", "inbuilt-default").Debug("Loaded config")
 | 
						|
	for _, path := range paths {
 | 
						|
		err := c.LoadConfigFromFile(path)
 | 
						|
		if err != nil {
 | 
						|
			log.WithError(err).Info("failed to load config, skipping")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	err = c.fromEnv()
 | 
						|
	if err != nil {
 | 
						|
		log.WithError(err).Info("failed to load env vars")
 | 
						|
	}
 | 
						|
	c.configureLogger()
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) LoadConfig(raw []byte) error {
 | 
						|
	err := yaml.Unmarshal(raw, c)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to parse YAML: %w", err)
 | 
						|
	}
 | 
						|
	c.walkScheme(c)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) LoadConfigFromFile(path string) error {
 | 
						|
	raw, err := os.ReadFile(path)
 | 
						|
	if err != nil {
 | 
						|
		if errors.Is(err, os.ErrNotExist) {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		return fmt.Errorf("failed to load config file: %w", err)
 | 
						|
	}
 | 
						|
	err = c.LoadConfig(raw)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	log.WithField("path", path).Debug("Loaded config")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) fromEnv() error {
 | 
						|
	_, err := env.UnmarshalFromEnviron(c)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to load environment variables: %w", err)
 | 
						|
	}
 | 
						|
	c.walkScheme(c)
 | 
						|
	log.Debug("Loaded config from environment")
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) walkScheme(v interface{}) {
 | 
						|
	rv := reflect.ValueOf(v)
 | 
						|
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	rv = rv.Elem()
 | 
						|
	if rv.Kind() != reflect.Struct {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	t := rv.Type()
 | 
						|
	for i := 0; i < rv.NumField(); i++ {
 | 
						|
		valueField := rv.Field(i)
 | 
						|
		switch valueField.Kind() {
 | 
						|
		case reflect.Struct:
 | 
						|
			if !valueField.Addr().CanInterface() {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			iface := valueField.Addr().Interface()
 | 
						|
			c.walkScheme(iface)
 | 
						|
		}
 | 
						|
 | 
						|
		typeField := t.Field(i)
 | 
						|
		if typeField.Type.Kind() != reflect.String {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		valueField.SetString(c.parseScheme(valueField.String()))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) parseScheme(rawVal string) string {
 | 
						|
	u, err := url.Parse(rawVal)
 | 
						|
	if err != nil {
 | 
						|
		return rawVal
 | 
						|
	}
 | 
						|
	if u.Scheme == "env" {
 | 
						|
		e, ok := os.LookupEnv(u.Host)
 | 
						|
		if ok {
 | 
						|
			return e
 | 
						|
		}
 | 
						|
		return u.RawQuery
 | 
						|
	} else if u.Scheme == "file" {
 | 
						|
		d, err := os.ReadFile(u.Path)
 | 
						|
		if err != nil {
 | 
						|
			return u.RawQuery
 | 
						|
		}
 | 
						|
		return strings.TrimSpace(string(d))
 | 
						|
	}
 | 
						|
	return rawVal
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) configureLogger() {
 | 
						|
	switch strings.ToLower(c.LogLevel) {
 | 
						|
	case "trace":
 | 
						|
		log.SetLevel(log.TraceLevel)
 | 
						|
	case "debug":
 | 
						|
		log.SetLevel(log.DebugLevel)
 | 
						|
	case "info":
 | 
						|
		log.SetLevel(log.InfoLevel)
 | 
						|
	case "warning":
 | 
						|
		log.SetLevel(log.WarnLevel)
 | 
						|
	case "error":
 | 
						|
		log.SetLevel(log.ErrorLevel)
 | 
						|
	default:
 | 
						|
		log.SetLevel(log.DebugLevel)
 | 
						|
	}
 | 
						|
 | 
						|
	fm := log.FieldMap{
 | 
						|
		log.FieldKeyMsg:  "event",
 | 
						|
		log.FieldKeyTime: "timestamp",
 | 
						|
	}
 | 
						|
 | 
						|
	if c.Debug {
 | 
						|
		log.SetFormatter(&log.TextFormatter{FieldMap: fm})
 | 
						|
	} else {
 | 
						|
		log.SetFormatter(&log.JSONFormatter{FieldMap: fm, DisableHTMLEscape: true})
 | 
						|
	}
 | 
						|
}
 |