@@ -6,49 +6,63 @@ import (
66 "log"
77 "net"
88 "net/http"
9− "net/url"
109 "os"
1110 "strings"
1211 "time"
13−)
1412
15−const VERSION = "0.1.0"
16−
17−var (
18− showVersion = flag.Bool("version", false, "print version string")
19− httpAddr = flag.String("http-address", "127.0.0.1:4180", "<addr>:<port> to listen on for HTTP clients")
20− redirectUrl = flag.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
21− clientID = flag.String("client-id", "", "the Google OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
22− clientSecret = flag.String("client-secret", "", "the OAuth Client Secret")
23− passBasicAuth = flag.Bool("pass-basic-auth", true, "pass HTTP Basic Auth information to upstream")
24− htpasswdFile = flag.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")
25− cookieSecret = flag.String("cookie-secret", "", "the seed string for secure cookies")
26− cookieDomain = flag.String("cookie-domain", "", "an optional cookie domain to force cookies to")
27− cookieExpire = flag.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
28− cookieHttpsOnly = flag.Bool("cookie-https-only", false, "set HTTPS only cookie")
29− authenticatedEmailsFile = flag.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
30− googleAppsDomains = StringArray{}
31− upstreams = StringArray{}
13+ "github.com/BurntSushi/toml"
14+ "github.com/mreiferson/go-options"
3215)
3316
34−func init() {
35− flag.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given google apps domain (may be given multiple times)")
36− flag.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
37−}
38−
3917func main() {
18+ flagSet := flag.NewFlagSet("google_auth_proxy", flag.ExitOnError)
19+
20+ googleAppsDomains := StringArray{}
21+ upstreams := StringArray{}
22+
23+ config := flagSet.String("config", "", "path to config file")
24+ showVersion := flagSet.Bool("version", false, "print version string")
25+
26+ flagSet.String("http-address", "127.0.0.1:4180", "<addr>:<port> to listen on for HTTP clients")
27+ flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
28+ flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
29+ flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
30+
31+ flagSet.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given Google apps domain (may be given multiple times)")
32+ flagSet.String("client-id", "", "the Google OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
33+ flagSet.String("client-secret", "", "the OAuth Client Secret")
34+ flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
35+ flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")
4036
41− flag.Parse()
37+ flagSet.String("cookie-secret", "", "the seed string for secure cookies")
38+ flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)")
39+ flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
40+ flagSet.Bool("cookie-https-only", false, "set HTTPS only cookie")
41+
42+ flagSet.Parse(os.Args[1:])
43+
44+ opts := NewOptions()
45+
46+ var cfg map[string]interface{}
47+ if *config != "" {
48+ _, err := toml.DecodeFile(*config, &cfg)
49+ if err != nil {
50+ log.Fatalf("ERROR: failed to load config file %s - %s", *config, err)
51+ }
52+ }
53+
54+ options.Resolve(opts, flagSet, cfg)
4255
4356 // Try to use env for secrets if no flag is set
44− if *clientID == "" {
45− *clientID = os.Getenv("google_auth_client_id")
57+ // TODO: better parsing of these values
58+ if opts.ClientID == "" {
59+ opts.ClientID = os.Getenv("google_auth_client_id")
4660 }
47− if *clientSecret == "" {
48− *clientSecret = os.Getenv("google_auth_secret")
61+ if opts.ClientSecret == "" {
62+ opts.ClientSecret = os.Getenv("google_auth_secret")
4963 }
50− if *cookieSecret == "" {
51− *cookieSecret = os.Getenv("google_auth_cookie_secret")
64+ if opts.CookieSecret == "" {
65+ opts.CookieSecret = os.Getenv("google_auth_cookie_secret")
5266 }
5367
5468 if *showVersion {
@@ -56,59 +70,41 @@ func main() {
5670 return
5771 }
5872
59− if len(upstreams) < 1 {
60− log.Fatal("missing --upstream")
61− }
62− if *cookieSecret == "" {
63− log.Fatal("missing --cookie-secret")
64− }
65− if *clientID == "" {
66− log.Fatal("missing --client-id")
67− }
68− if *clientSecret == "" {
69− log.Fatal("missing --client-secret")
70− }
71−
72− var upstreamUrls []*url.URL
73− for _, u := range upstreams {
74− upstreamUrl, err := url.Parse(u)
75− if err != nil {
76− log.Fatalf("error parsing --upstream %s", err.Error())
77− }
78− upstreamUrls = append(upstreamUrls, upstreamUrl)
79− }
80− redirectUrl, err := url.Parse(*redirectUrl)
73+ err := opts.Validate()
8174 if err != nil {
82− log.Fatalf("error parsing --redirect-url %s", err.Error())
75+ log.Printf("%s", err)
76+ os.Exit(1)
8377 }
8478
85− validator := NewValidator(googleAppsDomains, *authenticatedEmailsFile)
86− oauthproxy := NewOauthProxy(upstreamUrls, *clientID, *clientSecret, validator)
87− oauthproxy.SetRedirectUrl(redirectUrl)
88− if len(googleAppsDomains) != 0 && *authenticatedEmailsFile == "" {
89− if len(googleAppsDomains) > 1 {
90− oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(googleAppsDomains, ", "))
79+ validator := NewValidator(opts.GoogleAppsDomains, opts.AuthenticatedEmailsFile)
80+ oauthproxy := NewOauthProxy(opts, validator)
81+
82+ if len(opts.GoogleAppsDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
83+ if len(opts.GoogleAppsDomains) > 1 {
84+ oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.GoogleAppsDomains, ", "))
9185 } else {
92− oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", googleAppsDomains[0])
86+ oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.GoogleAppsDomains[0])
9387 }
9488 }
95− if *htpasswdFile != "" {
96− oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(*htpasswdFile)
89+
90+ if opts.HtpasswdFile != "" {
91+ oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile)
9792 if err != nil {
98− log.Fatalf("FATAL: unable to open %s %s", *htpasswdFile, err.Error())
93+ log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err)
9994 }
10095 }
101− listener, err := net.Listen("tcp", *httpAddr)
96+
97+ listener, err := net.Listen("tcp", opts.HttpAddress)
10298 if err != nil {
103− log.Fatalf("FATAL: listen (%s) failed - %s", *httpAddr, err.Error())
99+ log.Fatalf("FATAL: listen (%s) failed - %s", opts.HttpAddress, err)
104100 }
105− log.Printf("listening on %s", *httpAddr)
101+ log.Printf("listening on %s", opts.HttpAddress)
106102
107103 server := &http.Server{Handler: oauthproxy}
108104 err = server.Serve(listener)
109105 if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
110− log.Printf("ERROR: http.Serve() - %s", err.Error())
106+ log.Printf("ERROR: http.Serve() - %s", err)
111107 }
112108
113− log.Printf("HTTP: closing %s", listener.Addr().String())
109+ log.Printf("HTTP: closing %s", listener.Addr())
114110}