From 9060feb436a5fd00424df345f404a45aad568927 Mon Sep 17 00:00:00 2001 From: Jehiah Czebotar Date: Sun, 9 Nov 2014 21:07:02 -0500 Subject: [PATCH] better environment parsing --- README.md | 2 +- env_options.go | 33 +++++++++++++++++++++++++++++++++ htpasswd.go | 1 - main.go | 27 ++++++++------------------- oauthproxy.go | 1 + options.go | 22 +++++++++++----------- validator.go | 3 ++- 7 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 env_options.go diff --git a/README.md b/README.md index 63ec13c..9594637 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Usage of google_auth_proxy: ### Environment variables -The environment variables `google_auth_client_id`, `google_auth_secret` and `google_auth_cookie_secret` can be used in place of the corresponding command-line arguments. +The environment variables `GOOGLE_AUTH_PROXY_CLIENT_ID`, `GOOGLE_AUTH_PROXY_CLIENT_SECRET`, `GOOGLE_AUTH_PROXY_COOKIE_SECRET`, `GOOGLE_AUTH_PROXY_COOKIE_DOMAIN` and `GOOGLE_AUTH_PROXY_COOKIE_EXPIRE` can be used in place of the corresponding command-line arguments. ### Example Nginx Configuration diff --git a/env_options.go b/env_options.go new file mode 100644 index 0000000..2016ff8 --- /dev/null +++ b/env_options.go @@ -0,0 +1,33 @@ +package main + +import ( + "os" + "reflect" + "strings" +) + +func LoadOptionsFromEnv(options interface{}, cfg map[string]interface{}) { + val := reflect.ValueOf(options).Elem() + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + // pull out the struct tags: + // flag - the name of the command line flag + // deprecated - (optional) the name of the deprecated command line flag + // cfg - (optional, defaults to underscored flag) the name of the config file option + field := typ.Field(i) + flagName := field.Tag.Get("flag") + envName := field.Tag.Get("env") + cfgName := field.Tag.Get("cfg") + if cfgName == "" && flagName != "" { + cfgName = strings.Replace(flagName, "-", "_", -1) + } + if envName == "" || cfgName == "" { + // resolvable fields must have the `env` and `cfg` struct tag + continue + } + v := os.Getenv(envName) + if v != "" { + cfg[cfgName] = v + } + } +} diff --git a/htpasswd.go b/htpasswd.go index a620d58..5988c7c 100644 --- a/htpasswd.go +++ b/htpasswd.go @@ -17,7 +17,6 @@ type HtpasswdFile struct { } func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { - log.Printf("using htpasswd file %s", path) r, err := os.Open(path) if err != nil { return nil, err diff --git a/main.go b/main.go index 6d3f052..17d4adb 100644 --- a/main.go +++ b/main.go @@ -35,12 +35,17 @@ func main() { flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption") flagSet.String("cookie-secret", "", "the seed string for secure cookies") - flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)") + flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*") flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie") flagSet.Bool("cookie-https-only", false, "set HTTPS only cookie") flagSet.Parse(os.Args[1:]) + if *showVersion { + fmt.Printf("google_auth_proxy v%s\n", VERSION) + return + } + opts := NewOptions() var cfg map[string]interface{} @@ -50,26 +55,9 @@ func main() { log.Fatalf("ERROR: failed to load config file %s - %s", *config, err) } } - + LoadOptionsFromEnv(opts, cfg) options.Resolve(opts, flagSet, cfg) - // Try to use env for secrets if no flag is set - // TODO: better parsing of these values - if opts.ClientID == "" { - opts.ClientID = os.Getenv("google_auth_client_id") - } - if opts.ClientSecret == "" { - opts.ClientSecret = os.Getenv("google_auth_secret") - } - if opts.CookieSecret == "" { - opts.CookieSecret = os.Getenv("google_auth_cookie_secret") - } - - if *showVersion { - fmt.Printf("google_auth_proxy v%s\n", VERSION) - return - } - err := opts.Validate() if err != nil { log.Printf("%s", err) @@ -88,6 +76,7 @@ func main() { } if opts.HtpasswdFile != "" { + log.Printf("using htpasswd file %s", opts.HtpasswdFile) oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile) if err != nil { log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err) diff --git a/oauthproxy.go b/oauthproxy.go index e726780..73f04fa 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -54,6 +54,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { redirectUrl := opts.redirectUrl redirectUrl.Path = oauthCallbackPath + log.Printf("OauthProxy configured for %s", opts.ClientID) return &OauthProxy{ CookieKey: "_oauthproxy", CookieSeed: opts.CookieSecret, diff --git a/options.go b/options.go index c2a7b21..2d82969 100644 --- a/options.go +++ b/options.go @@ -11,13 +11,13 @@ import ( type Options struct { HttpAddress string `flag:"http-address" cfg:"http_address"` RedirectUrl string `flag:"redirect-url" cfg:"redirect_url"` - ClientID string `flag:"client-id" cfg:"client_id"` - ClientSecret string `flag:"client-secret" cfg:"client_secret"` + ClientID string `flag:"client-id" cfg:"client_id" env:"GOOGLE_AUTH_PROXY_CLIENT_ID"` + ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"GOOGLE_AUTH_PROXY_CLIENT_SECRET"` PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"` - CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret"` - CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain"` - CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire"` + CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"` + CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"` + CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"` CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"` GoogleAppsDomains []string `flag:"google-apps-domain" cfg:"google_apps_domains"` @@ -34,28 +34,28 @@ func NewOptions() *Options { func (o *Options) Validate() error { if len(o.Upstreams) < 1 { - return errors.New("missing -upstream") + return errors.New("missing setting: upstream") } if o.CookieSecret == "" { - errors.New("missing -cookie-secret") + errors.New("missing setting: cookie-secret") } if o.ClientID == "" { - return errors.New("missing -client-id") + return errors.New("missing setting: client-id") } if o.ClientSecret == "" { - return errors.New("missing -client-secret") + return errors.New("missing setting: client-secret") } redirectUrl, err := url.Parse(o.RedirectUrl) if err != nil { - return fmt.Errorf("error parsing -redirect-url=%q %s", o.RedirectUrl, err) + return fmt.Errorf("error parsing redirect-url=%q %s", o.RedirectUrl, err) } o.redirectUrl = redirectUrl for _, u := range o.Upstreams { upstreamUrl, err := url.Parse(u) if err != nil { - return fmt.Errorf("error parsing -upstream=%q %s", upstreamUrl, err) + return fmt.Errorf("error parsing upstream=%q %s", upstreamUrl, err) } if upstreamUrl.Path == "" { upstreamUrl.Path = "/" diff --git a/validator.go b/validator.go index 4caa5ec..81a1bf8 100644 --- a/validator.go +++ b/validator.go @@ -12,9 +12,10 @@ func NewValidator(domains []string, usersFile string) func(string) bool { validUsers := make(map[string]bool) if usersFile != "" { + log.Printf("using authenticated emails file %s", usersFile) r, err := os.Open(usersFile) if err != nil { - log.Fatalf("failed opening -authenticated-emails-file=%v, %s", usersFile, err.Error()) + log.Fatalf("failed opening authenticated-emails-file=%q, %s", usersFile, err) } csv_reader := csv.NewReader(r) csv_reader.Comma = ','